|
| 1 | +import { Callback, IPluginAuth, Logger } from '@verdaccio/types'; |
| 2 | +import ActiveDirectory from 'activedirectory2'; |
| 3 | + |
| 4 | +export const NotAuthMessage = 'AD - Active Directory authentication failed'; |
| 5 | + |
| 6 | +export interface ActiveDirectoryConfig { |
| 7 | + url: string; |
| 8 | + baseDN: string; |
| 9 | + domainSuffix: string; |
| 10 | + groupName?: string | string[]; |
| 11 | +} |
| 12 | + |
| 13 | +class ActiveDirectoryPlugin implements IPluginAuth<ActiveDirectoryConfig> { |
| 14 | + private config: ActiveDirectoryConfig; |
| 15 | + private logger: Logger; |
| 16 | + |
| 17 | + public constructor(config: ActiveDirectoryConfig, opts: { logger: Logger }) { |
| 18 | + this.config = config; |
| 19 | + this.logger = opts.logger; |
| 20 | + } |
| 21 | + |
| 22 | + public authenticate(user: string, password: string, cb: Callback): void { |
| 23 | + const username = `${user}@${this.config.domainSuffix}`; |
| 24 | + |
| 25 | + const connectionConfig = { |
| 26 | + ...this.config, |
| 27 | + domainSuffix: undefined, |
| 28 | + username, |
| 29 | + password, |
| 30 | + }; |
| 31 | + |
| 32 | + const connection = new ActiveDirectory(connectionConfig); |
| 33 | + |
| 34 | + connection.authenticate(username, password, (err, isAuthenticated): void => { |
| 35 | + if (err) { |
| 36 | + this.logger.warn(`AD - Active Directory authentication failed with error: ${err}`); |
| 37 | + return cb(err); |
| 38 | + } |
| 39 | + |
| 40 | + if (!isAuthenticated) { |
| 41 | + this.logger.warn(NotAuthMessage); |
| 42 | + return cb(new Error(NotAuthMessage)); |
| 43 | + } |
| 44 | + |
| 45 | + const { groupName } = this.config; |
| 46 | + if (!groupName) { |
| 47 | + this.logger.info('AD - Active Directory authentication succeeded'); |
| 48 | + cb(null, [user]); |
| 49 | + } else { |
| 50 | + // TODO check for updates on @types/activedirectory2 or add types for this fn |
| 51 | + // @ts-ignore |
| 52 | + connection.getGroupMembershipForUser(username, (err, groups: object[]): void => { |
| 53 | + if (err) { |
| 54 | + this.logger.warn(`AD - Active Directory group check failed with error: ${err}`); |
| 55 | + return cb(err); |
| 56 | + } |
| 57 | + |
| 58 | + const requestedGroups = Array.isArray(groupName) ? groupName : [groupName]; |
| 59 | + const matchingGroups = requestedGroups.filter((requestedGroup): boolean => |
| 60 | + groups.some((group: any): boolean => requestedGroup === group.cn || requestedGroup === group.dn) |
| 61 | + ); |
| 62 | + |
| 63 | + if (matchingGroups.length <= 0) { |
| 64 | + const notMemberMessage = `AD - User ${user} is not member of group(s): ${requestedGroups.join(', ')}`; |
| 65 | + |
| 66 | + this.logger.warn(notMemberMessage); |
| 67 | + cb(new Error(notMemberMessage)); |
| 68 | + } else { |
| 69 | + this.logger.info( |
| 70 | + `AD - Active Directory authentication succeeded in group(s): ${matchingGroups.join(', ')}` |
| 71 | + ); |
| 72 | + cb(null, [...matchingGroups, user]); |
| 73 | + } |
| 74 | + }); |
| 75 | + } |
| 76 | + }); |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +export default ActiveDirectoryPlugin; |
0 commit comments