1- const LdapStrategy = require ( 'passport-ldapauth' )
2- , log = require ( 'winston' )
3- , User = require ( '../models/user' )
4- , Role = require ( '../models/role' )
5- , TokenAssertion = require ( './verification' ) . TokenAssertion
6- , api = require ( '../api' )
7- , userTransformer = require ( '../transformers/user' )
8- , { app, passport, tokenService } = require ( './index' ) ;
1+ import passport from 'passport'
2+ import LdapStrategy from 'passport-ldapauth'
3+ import { IdentityProvider , IdentityProviderUser } from './ingress.entities'
4+ import { IngressProtocolWebBinding , IngressResponseType } from './ingress.protocol.bindings'
95
10- function configure ( strategy ) {
11- log . info ( 'Configuring ' + strategy . title + ' authentication' ) ;
126
13- passport . use ( strategy . name , new LdapStrategy ( {
14- server : {
15- url : strategy . settings . url ,
16- bindDN : strategy . settings . bindDN ,
17- bindCredentials : strategy . settings . bindCredentials ,
18- searchBase : strategy . settings . searchBase ,
19- searchFilter : strategy . settings . searchFilter ,
20- searchScope : strategy . settings . searchScope ,
21- groupSearchBase : strategy . settings . groupSearchBase ,
22- groupSearchFilter : strategy . settings . groupSearchFilter ,
23- groupSearchScope : strategy . settings . groupSearchScope ,
24- bindProperty : strategy . settings . bindProperty ,
25- groupDnProperty : strategy . settings . groupDnProperty
26- }
27- } ,
28- function ( profile , done ) {
29- const username = profile [ strategy . settings . profile . id ] ;
30- // TODO: users-next
31- User . getUserByAuthenticationStrategy ( strategy . type , username , function ( err , user ) {
32- if ( err ) return done ( err ) ;
33-
34- if ( ! user ) {
35- // Create an account for the user
36- Role . getRole ( 'USER_ROLE' , function ( err , role ) {
37- if ( err ) return done ( err ) ;
7+ type LdapProfileKeys = {
8+ id ?: string
9+ email ?: string
10+ displayName ?: string
11+ }
3812
39- const user = {
40- username : username ,
41- displayName : profile [ strategy . settings . profile . displayName ] ,
42- email : profile [ strategy . settings . profile . email ] ,
43- active : false ,
44- roleId : role . _id ,
45- authentication : {
46- type : strategy . name ,
47- id : username ,
48- authenticationConfiguration : {
49- name : strategy . name
50- }
51- }
52- } ;
53- // TODO: users-next
54- new api . User ( ) . create ( user ) . then ( newUser => {
55- if ( ! newUser . authentication . authenticationConfiguration . enabled ) {
56- log . warn ( newUser . authentication . authenticationConfiguration . title + " authentication is not enabled" ) ;
57- return done ( null , newUser , { message : 'Authentication method is not enabled, please contact a MAGE administrator for assistance.' } ) ;
58- }
59- if ( newUser . active ) {
60- done ( null , newUser ) ;
61- } else {
62- done ( null , newUser , { status : 403 } ) ;
63- }
64- } ) . catch ( err => done ( err ) ) ;
65- } ) ;
66- } else if ( ! user . authentication . authenticationConfiguration . enabled ) {
67- log . warn ( user . authentication . authenticationConfiguration . title + " authentication is not enabled" ) ;
68- return done ( null , user , { message : 'Authentication method is not enabled, please contact a MAGE administrator for assistance.' } ) ;
69- } else {
70- return done ( null , user ) ;
71- }
72- } ) ;
73- } )
74- ) ;
13+ type LdapProtocolSettings = LdapStrategy . Options [ 'server' ] & {
14+ profile ?: LdapProfileKeys
7515}
7616
77- function setDefaults ( strategy ) {
78- if ( ! strategy . settings . profile ) {
79- strategy . settings . profile = { } ;
17+ type ReadyLdapProtocolSettings = Omit < LdapProtocolSettings , 'profile' > & { profile : Required < LdapProfileKeys > }
18+
19+ function copyProtocolSettings ( from : LdapProtocolSettings ) : LdapProtocolSettings {
20+ const copy = { ...from }
21+ if ( copy . profile ) {
22+ copy . profile = { ...from . profile ! }
8023 }
81- if ( ! strategy . settings . profile . displayName ) {
82- strategy . settings . profile . displayName = 'givenname' ;
24+ return copy
25+ }
26+
27+ function applyDefaultProtocolSettings ( idp : IdentityProvider ) : ReadyLdapProtocolSettings {
28+ const settings = copyProtocolSettings ( idp . protocolSettings as LdapProtocolSettings )
29+ const profileKeys = settings . profile || { }
30+ if ( ! profileKeys . displayName ) {
31+ profileKeys . displayName = 'givenname' ;
8332 }
84- if ( ! strategy . settings . profile . email ) {
85- strategy . settings . profile . email = 'mail' ;
33+ if ( ! profileKeys . email ) {
34+ profileKeys . email = 'mail' ;
8635 }
87- if ( ! strategy . settings . profile . id ) {
88- strategy . settings . profile . id = 'cn' ;
36+ if ( ! profileKeys . id ) {
37+ profileKeys . id = 'cn' ;
8938 }
39+ settings . profile = profileKeys
40+ return settings as ReadyLdapProtocolSettings
9041}
9142
92- function initialize ( strategy ) {
93- setDefaults ( strategy ) ;
94- configure ( strategy ) ;
95-
96- const authenticationOptions = {
97- invalidLogonHours : `Not Permitted to login to ${ strategy . title } account at this time.` ,
98- invalidWorkstation : `Not permited to logon to ${ strategy . title } account at this workstation.` ,
99- passwordExpired : `${ strategy . title } password expired.` ,
100- accountDisabled : `${ strategy . title } account disabled.` ,
101- accountExpired : `${ strategy . title } account expired.` ,
102- passwordMustChange : `User must reset ${ strategy . title } password.` ,
103- accountLockedOut : `${ strategy . title } user account locked.` ,
104- invalidCredentials : `Invalid ${ strategy . title } username/password.`
105- } ;
106-
107- app . post ( `/auth/${ strategy . name } /signin` ,
108- function authenticate ( req , res , next ) {
109- passport . authenticate ( strategy . name , authenticationOptions , function ( err , user , info = { } ) {
110- if ( err ) return next ( err ) ;
111-
112- if ( ! user ) {
113- return res . status ( 401 ) . send ( info . message ) ;
114- }
115-
116- if ( ! user . active ) {
117- return res . status ( info . status || 401 ) . send ( 'User account is not approved, please contact your MAGE administrator to approve your account.' ) ;
118- }
119-
120- if ( ! user . enabled ) {
121- log . warn ( 'Failed user login attempt: User ' + user . username + ' account is disabled.' ) ;
122- return res . status ( 401 ) . send ( 'Your account has been disabled, please contact a MAGE administrator for assistance.' )
123- }
43+ function strategyOptionsFromProtocolSettings ( settings : ReadyLdapProtocolSettings ) : LdapStrategy . Options {
44+ return {
45+ server : {
46+ url : settings . url ,
47+ bindDN : settings . bindDN ,
48+ bindCredentials : settings . bindCredentials ,
49+ searchBase : settings . searchBase ,
50+ searchFilter : settings . searchFilter ,
51+ searchScope : settings . searchScope ,
52+ groupSearchBase : settings . groupSearchBase ,
53+ groupSearchFilter : settings . groupSearchFilter ,
54+ groupSearchScope : settings . groupSearchScope ,
55+ bindProperty : settings . bindProperty ,
56+ groupDnProperty : settings . groupDnProperty
57+ }
58+ }
59+ }
12460
125- if ( ! user . authentication . authenticationConfigurationId ) {
126- log . warn ( 'Failed user login attempt: ' + user . authentication . type + ' is not configured' ) ;
127- return res . status ( 401 ) . send ( user . authentication . type + ' authentication is not configured, please contact a MAGE administrator for assistance.' )
61+ export function createWebBinding ( idp : IdentityProvider , passport : passport . Authenticator ) : IngressProtocolWebBinding {
62+ const settings = applyDefaultProtocolSettings ( idp )
63+ const profileKeys = settings . profile
64+ const strategyOptions = strategyOptionsFromProtocolSettings ( settings )
65+ const verify : LdapStrategy . VerifyCallback = ( profile , done ) => {
66+ const idpAccount : IdentityProviderUser = {
67+ username : profile [ profileKeys . id ] ,
68+ displayName : profile [ profileKeys . displayName ] ,
69+ email : profile [ profileKeys . email ] ,
70+ phones : [ ] ,
71+ idpAccountId : profile [ profileKeys . id ]
72+ }
73+ const webIngressUser : Express . User = {
74+ admittingFromIdentityProvider : {
75+ account : idpAccount ,
76+ idpName : idp . name ,
77+ }
78+ }
79+ return done ( null , webIngressUser )
80+ }
81+ const title = idp . title
82+ const authOptions : LdapStrategy . AuthenticateOptions = {
83+ invalidLogonHours : `Access to ${ title } account is prohibited at this time.` ,
84+ invalidWorkstation : `Access to ${ title } account is prohibited from this workstation.` ,
85+ passwordExpired : `${ title } password expired.` ,
86+ accountDisabled : `${ title } account disabled.` ,
87+ accountExpired : `${ title } account expired.` ,
88+ passwordMustChange : `${ title } account requires password reset.` ,
89+ accountLockedOut : `${ title } account locked.` ,
90+ invalidCredentials : `Invalid ${ title } credentials.` ,
91+ }
92+ const ldapIdp = new LdapStrategy ( strategyOptions , verify )
93+ return {
94+ ingressResponseType : IngressResponseType . Direct ,
95+ beginIngressFlow ( req , res , next , flowState ) : any {
96+ const completeIngress : passport . AuthenticateCallback = ( err , user ) => {
97+ if ( err ) {
98+ return next ( err )
12899 }
129-
130- if ( ! user . authentication . authenticationConfiguration . enabled ) {
131- log . warn ( 'Failed user login attempt: Authentication ' + user . authentication . authenticationConfiguration . title + ' is disabled.' ) ;
132- return res . status ( 401 ) . send ( user . authentication . authenticationConfiguration . title + ' authentication is disabled, please contact a MAGE administrator for assistance.' )
100+ if ( user && user . admittingFromIdentityProvider ) {
101+ user . admittingFromIdentityProvider . flowState = flowState
102+ req . user = user
103+ return next ( )
133104 }
134-
135- tokenService . generateToken ( user . _id . toString ( ) , TokenAssertion . Authorized , 60 * 5 )
136- . then ( token => {
137- res . json ( {
138- user : userTransformer . transform ( req . user , { path : req . getRoot ( ) } ) ,
139- token : token
140- } ) ;
141- } ) . catch ( err => next ( err ) ) ;
142- } ) ( req , res , next ) ;
105+ return res . status ( 500 ) . send ( 'internal server error: invalid ldap ingress state' )
106+ }
107+ passport . authenticate ( ldapIdp , authOptions , completeIngress ) ( req , res , next )
108+ } ,
109+ handleIngressFlowRequest ( req , res ) : any {
110+ return res . status ( 400 ) . send ( 'invalid ldap ingress request' )
143111 }
144- ) ;
145- } ;
146-
147- module . exports = {
148- initialize
112+ }
149113}
0 commit comments