Skip to content

Commit 8ec3609

Browse files
committed
refactor(service): users/auth: migrate ldap protocol to new ingress scheme
1 parent 54d4f59 commit 8ec3609

File tree

1 file changed

+97
-133
lines changed

1 file changed

+97
-133
lines changed
Lines changed: 97 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,113 @@
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

Comments
 (0)