fix(xo-server-auth-ldap): create logger inside plugin (#5864)

The plugin was wrongly expecting a logger instance to be passed on instantiation
This commit is contained in:
Pierre Donias 2021-08-11 11:21:22 +02:00 committed by GitHub
parent 656dc8fefc
commit d5f5cdd27a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 26 deletions

View File

@ -31,6 +31,7 @@
"node": ">=10" "node": ">=10"
}, },
"dependencies": { "dependencies": {
"@xen-orchestra/log": "^0.2.1",
"exec-promise": "^0.7.0", "exec-promise": "^0.7.0",
"inquirer": "^8.0.0", "inquirer": "^8.0.0",
"ldapts": "^2.2.1", "ldapts": "^2.2.1",

View File

@ -3,9 +3,12 @@
import ensureArray from 'ensure-array' import ensureArray from 'ensure-array'
import fromCallback from 'promise-toolbox/fromCallback' import fromCallback from 'promise-toolbox/fromCallback'
import { Client } from 'ldapts' import { Client } from 'ldapts'
import { createLogger } from '@xen-orchestra/log'
import { Filter } from 'ldapts/filters/Filter' import { Filter } from 'ldapts/filters/Filter'
import { readFile } from 'fs' import { readFile } from 'fs'
const logger = createLogger('xo:xo-server-auth-ldap')
// =================================================================== // ===================================================================
const DEFAULTS = { const DEFAULTS = {
@ -27,8 +30,6 @@ const evalFilter = (filter, vars) =>
return escape(value) return escape(value)
}) })
const noop = Function.prototype
export const configurationSchema = { export const configurationSchema = {
type: 'object', type: 'object',
properties: { properties: {
@ -184,8 +185,7 @@ export const testSchema = {
// =================================================================== // ===================================================================
class AuthLdap { class AuthLdap {
constructor({ logger = noop, xo }) { constructor({ xo } = {}) {
this._logger = logger
this._xo = xo this._xo = xo
this._authenticate = this._authenticate.bind(this) this._authenticate = this._authenticate.bind(this)
@ -257,10 +257,8 @@ class AuthLdap {
} }
async _authenticate({ username, password }) { async _authenticate({ username, password }) {
const logger = this._logger
if (username === undefined || password === undefined) { if (username === undefined || password === undefined) {
logger('require `username` and `password` to authenticate!') logger.debug('require `username` and `password` to authenticate!')
return null return null
} }
@ -276,29 +274,34 @@ class AuthLdap {
{ {
const { _credentials: credentials } = this const { _credentials: credentials } = this
if (credentials) { if (credentials) {
logger(`attempting to bind with as ${credentials.dn}...`) logger.debug(`attempting to bind with as ${credentials.dn}...`)
await client.bind(credentials.dn, credentials.password) await client.bind(credentials.dn, credentials.password)
logger(`successfully bound as ${credentials.dn}`) logger.debug(`successfully bound as ${credentials.dn}`)
} }
} }
// Search for the user. // Search for the user.
logger('searching for entries...') logger.debug('searching for entries...')
const { searchEntries: entries } = await client.search(this._searchBase, { const { searchEntries: entries } = await client.search(this._searchBase, {
scope: 'sub', scope: 'sub',
filter: evalFilter(this._searchFilter, { filter: evalFilter(this._searchFilter, {
name: username, name: username,
}), }),
}) })
logger(`${entries.length} entries found`) logger.debug(`${entries.length} entries found`)
// Try to find an entry which can be bind with the given password. // Try to find an entry which can be bind with the given password.
for (const entry of entries) { for (const entry of entries) {
try { try {
logger(`attempting to bind as ${entry.dn}`) logger.debug(`attempting to bind as ${entry.dn}`)
await client.bind(entry.dn, password) await client.bind(entry.dn, password)
logger(`successfully bound as ${entry.dn} => ${username} authenticated`) logger.info(`successfully bound as ${entry.dn} => ${username} authenticated`)
logger(JSON.stringify(entry, null, 2)) logger.debug(JSON.stringify(entry, null, 2))
// CLI test: don't register user/sync groups
if (this._xo === undefined) {
return
}
let user let user
if (this._userIdAttribute === undefined) { if (this._userIdAttribute === undefined) {
@ -315,18 +318,18 @@ class AuthLdap {
try { try {
await this._synchronizeGroups(user, entry[groupsConfig.membersMapping.userAttribute]) await this._synchronizeGroups(user, entry[groupsConfig.membersMapping.userAttribute])
} catch (error) { } catch (error) {
logger(`failed to synchronize groups: ${error.message}`) logger.error(`failed to synchronize groups: ${error.message}`)
} }
} }
} }
return { userId: user.id } return { userId: user.id }
} catch (error) { } catch (error) {
logger(`failed to bind as ${entry.dn}: ${error.message}`) logger.debug(`failed to bind as ${entry.dn}: ${error.message}`)
} }
} }
logger(`could not authenticate ${username}`) logger.debug(`could not authenticate ${username}`)
return null return null
} finally { } finally {
await client.unbind() await client.unbind()
@ -335,7 +338,6 @@ class AuthLdap {
// Synchronize user's groups OR all groups if no user is passed // Synchronize user's groups OR all groups if no user is passed
async _synchronizeGroups(user, memberId) { async _synchronizeGroups(user, memberId) {
const logger = this._logger
const client = new Client(this._clientOpts) const client = new Client(this._clientOpts)
try { try {
@ -347,12 +349,12 @@ class AuthLdap {
{ {
const { _credentials: credentials } = this const { _credentials: credentials } = this
if (credentials) { if (credentials) {
logger(`attempting to bind with as ${credentials.dn}...`) logger.debug(`attempting to bind with as ${credentials.dn}...`)
await client.bind(credentials.dn, credentials.password) await client.bind(credentials.dn, credentials.password)
logger(`successfully bound as ${credentials.dn}`) logger.debug(`successfully bound as ${credentials.dn}`)
} }
} }
logger('syncing groups...') logger.info('syncing groups...')
const { base, displayNameAttribute, filter, idAttribute, membersMapping } = this._groupsConfig const { base, displayNameAttribute, filter, idAttribute, membersMapping } = this._groupsConfig
const { searchEntries: ldapGroups } = await client.search(base, { const { searchEntries: ldapGroups } = await client.search(base, {
scope: 'sub', scope: 'sub',
@ -374,7 +376,7 @@ class AuthLdap {
// Empty or undefined names/IDs are invalid // Empty or undefined names/IDs are invalid
if (!groupLdapId || !groupLdapName) { if (!groupLdapId || !groupLdapName) {
logger(`Invalid group ID (${groupLdapId}) or name (${groupLdapName})`) logger.error(`Invalid group ID (${groupLdapId}) or name (${groupLdapName})`)
continue continue
} }
@ -393,7 +395,7 @@ class AuthLdap {
if (xoGroupIndex === -1) { if (xoGroupIndex === -1) {
if (xoGroups.find(group => group.name === groupLdapName) !== undefined) { if (xoGroups.find(group => group.name === groupLdapName) !== undefined) {
// TODO: check against LDAP groups that are being created as well // TODO: check against LDAP groups that are being created as well
logger(`A group called ${groupLdapName} already exists`) logger.error(`A group called ${groupLdapName} already exists`)
continue continue
} }
xoGroup = await this._xo.createGroup({ xoGroup = await this._xo.createGroup({
@ -459,6 +461,8 @@ class AuthLdap {
xoGroups.filter(group => group.provider === 'ldap').map(group => this._xo.deleteGroup(group.id)) xoGroups.filter(group => group.provider === 'ldap').map(group => this._xo.deleteGroup(group.id))
) )
} }
logger.info('done syncing groups')
} finally { } finally {
await client.unbind() await client.unbind()
} }

View File

@ -1,6 +1,8 @@
#!/usr/bin/env node #!/usr/bin/env node
import execPromise from 'exec-promise' import execPromise from 'exec-promise'
import transportConsole from '@xen-orchestra/log/transports/console'
import { configure } from '@xen-orchestra/log/configure.js'
import { fromCallback } from 'promise-toolbox' import { fromCallback } from 'promise-toolbox'
import { readFile, writeFile } from 'fs' import { readFile, writeFile } from 'fs'
@ -28,9 +30,14 @@ execPromise(async args => {
} }
) )
const plugin = createPlugin({ configure([
logger: console.log.bind(console), {
}) filter: process.env.DEBUG ?? 'xo:xo-server-auth-ldap',
transport: transportConsole(),
},
])
const plugin = createPlugin()
await plugin.configure(config) await plugin.configure(config)
await plugin._authenticate({ await plugin._authenticate({