UNPKG

node-expose-sspi-strict

Version:

Expose the Microsoft Windows SSPI interface in order to do NTLM and Kerberos authentication.

126 lines (110 loc) 3.45 kB
import { sspi, CtxtHandle } from '../../lib/api'; import { getUser } from './userdb'; import dbg from 'debug'; import { sso } from '.'; import * as os from 'os'; import { AuthOptions, User } from './interfaces'; const debug = dbg('node-expose-sspi:SSO'); export type SSOMethod = 'NTLM' | 'Kerberos'; export class SSO { user: User; owner: User; private options: AuthOptions = { useActiveDirectory: true, useGroups: true, useOwner: true, groupFilterRegex: '.*', }; constructor( private serverContextHandle: CtxtHandle, public method?: SSOMethod ) {} async load(): Promise<void> { const names = sspi.QueryContextAttributes( this.serverContextHandle, 'SECPKG_ATTR_NAMES' ); const [domain, name] = names.sUserName.split('\\'); this.user = { domain, name }; // impersonate to retrieve the userToken. sspi.ImpersonateSecurityContext(this.serverContextHandle); debug('impersonate security context ok'); const userToken = sspi.OpenThreadToken(); debug('userToken: ', userToken); try { this.user.displayName = sspi.GetUserNameEx('NameDisplay'); } catch (e) { // exemple of error scenario: local user without displayname. this.user.displayName = this.user.name; } sspi.RevertSecurityContext(this.serverContextHandle); if (this.options.useGroups) { const groups = sspi.GetTokenInformation({ accessToken: userToken, tokenInformationClass: 'TokenGroups', filter: this.options.groupFilterRegex, }); groups.sort(); debug('groups: ', groups); this.user.groups = groups; } // free the userToken sspi.CloseHandle(userToken); const { sid } = sspi.LookupAccountName(names.sUserName); this.user.sid = sid; try { if ( sso.isOnDomain() && sso.isActiveDirectoryReachable() && os.hostname() !== domain && this.options.useActiveDirectory ) { const adUser = await getUser(`(sAMAccountName=${name})`); this.user.adUser = adUser; } } catch (e) { debug('cannot getUser from AD. e: ', e); } // owner info. if (this.options.useOwner) { const owner = sspi.GetUserName(); debug('owner: ', owner); this.owner = { name: owner }; try { this.owner.displayName = sspi.GetUserNameEx('NameDisplay'); } catch (e) { // exemple of error scenario: local user without displayname. this.owner.displayName = this.owner.name; } if (this.options.useGroups) { const processToken = sspi.OpenProcessToken([ 'TOKEN_QUERY', 'TOKEN_QUERY_SOURCE', ]); const ownerGroups = sspi.GetTokenInformation({ accessToken: processToken, tokenInformationClass: 'TokenGroups', filter: this.options.groupFilterRegex, }); ownerGroups.sort(); debug('ownerGroups: ', ownerGroups); this.owner.groups = ownerGroups; sspi.CloseHandle(processToken); } try { const o = sspi.LookupAccountName(owner); this.owner.sid = o.sid; this.owner.domain = o.domain; } catch (e) {} } } getJSON(): SSO { const json = { ...this }; delete json.options; delete json.serverContextHandle; return json; } setOptions(options: AuthOptions): void { Object.assign(this.options, options); } }