@hitchy/plugin-auth 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +9 -0
- package/api/model/authorization/rule.js +1 -1
- package/api/model/role.js +1 -1
- package/api/model/user.js +1 -1
- package/api/policy/authentication.js +44 -13
- package/api/policy/user.js +1 -1
- package/api/service/auth/manager.js +2 -2
- package/api/service/authentication/passport.js +3 -3
- package/api/service/authentication/strategies.js +4 -4
- package/api/service/authorization/node.js +25 -17
- package/api/service/authorization/tree.js +10 -8
- package/api/service/session.js +150 -0
- package/index.js +2 -2
- package/package.json +12 -12
- package/public/404.html +21 -0
- package/public/api/config.html +100 -0
- package/public/api/controller/index.html +24 -0
- package/public/api/controller/user.html +29 -0
- package/public/api/index.html +24 -0
- package/public/api/model/authorization-rule.html +24 -0
- package/public/api/model/index.html +24 -0
- package/public/api/model/role.html +24 -0
- package/public/api/model/user-to-role.html +24 -0
- package/public/api/model/user.html +24 -0
- package/public/api/policy/authentication.html +28 -0
- package/public/api/policy/authorization.html +31 -0
- package/public/api/policy/index.html +24 -0
- package/public/api/policy/user.html +24 -0
- package/public/api/routing.html +40 -0
- package/public/api/service/auth-manager.html +24 -0
- package/public/api/service/authentication-passport.html +24 -0
- package/public/api/service/authentication-strategies.html +24 -0
- package/public/api/service/authorization-node.html +24 -0
- package/public/api/service/authorization-policy-generator.html +42 -0
- package/public/api/service/authorization-tree.html +24 -0
- package/public/api/service/index.html +24 -0
- package/public/assets/api_config.md.BiPnBhyk.js +77 -0
- package/public/assets/api_config.md.BiPnBhyk.lean.js +1 -0
- package/public/assets/api_controller_index.md.mhiyhr_C.js +1 -0
- package/public/assets/api_controller_index.md.mhiyhr_C.lean.js +1 -0
- package/public/assets/api_controller_user.md.BiFYPTow.js +6 -0
- package/public/assets/api_controller_user.md.BiFYPTow.lean.js +1 -0
- package/public/assets/api_index.md.j6eBaebO.js +1 -0
- package/public/assets/api_index.md.j6eBaebO.lean.js +1 -0
- package/public/assets/api_model_authorization-rule.md.CFNqudsp.js +1 -0
- package/public/assets/api_model_authorization-rule.md.CFNqudsp.lean.js +1 -0
- package/public/assets/api_model_index.md.Dw3UH73J.js +1 -0
- package/public/assets/api_model_index.md.Dw3UH73J.lean.js +1 -0
- package/public/assets/api_model_role.md.DFCGXTBA.js +1 -0
- package/public/assets/api_model_role.md.DFCGXTBA.lean.js +1 -0
- package/public/assets/api_model_user-to-role.md.QNC96rs-.js +1 -0
- package/public/assets/api_model_user-to-role.md.QNC96rs-.lean.js +1 -0
- package/public/assets/api_model_user.md.C2GSzwZj.js +1 -0
- package/public/assets/api_model_user.md.C2GSzwZj.lean.js +1 -0
- package/public/assets/api_policy_authentication.md.Ccj8Rneb.js +5 -0
- package/public/assets/api_policy_authentication.md.Ccj8Rneb.lean.js +1 -0
- package/public/assets/api_policy_authorization.md.CP3y7VOT.js +8 -0
- package/public/assets/api_policy_authorization.md.CP3y7VOT.lean.js +1 -0
- package/public/assets/api_policy_index.md.CmaeRtru.js +1 -0
- package/public/assets/api_policy_index.md.CmaeRtru.lean.js +1 -0
- package/public/assets/api_policy_user.md.ePU_LHGT.js +1 -0
- package/public/assets/api_policy_user.md.ePU_LHGT.lean.js +1 -0
- package/public/assets/api_routing.md.BP98xeNw.js +17 -0
- package/public/assets/api_routing.md.BP98xeNw.lean.js +1 -0
- package/public/assets/api_service_auth-manager.md.CcpV6slZ.js +1 -0
- package/public/assets/api_service_auth-manager.md.CcpV6slZ.lean.js +1 -0
- package/public/assets/api_service_authentication-passport.md.DvhoW1TR.js +1 -0
- package/public/assets/api_service_authentication-passport.md.DvhoW1TR.lean.js +1 -0
- package/public/assets/api_service_authentication-strategies.md.DjDT2F9g.js +1 -0
- package/public/assets/api_service_authentication-strategies.md.DjDT2F9g.lean.js +1 -0
- package/public/assets/api_service_authorization-node.md.DAN4WdDZ.js +1 -0
- package/public/assets/api_service_authorization-node.md.DAN4WdDZ.lean.js +1 -0
- package/public/assets/api_service_authorization-policy-generator.md.IaQjgxfZ.js +19 -0
- package/public/assets/api_service_authorization-policy-generator.md.IaQjgxfZ.lean.js +1 -0
- package/public/assets/api_service_authorization-tree.md.I7ff4vao.js +1 -0
- package/public/assets/api_service_authorization-tree.md.I7ff4vao.lean.js +1 -0
- package/public/assets/api_service_index.md.Bfk1E4Zn.js +1 -0
- package/public/assets/api_service_index.md.Bfk1E4Zn.lean.js +1 -0
- package/public/assets/app.Bnek3cfe.js +1 -0
- package/public/assets/chunks/framework.BaHG-QLs.js +17 -0
- package/public/assets/chunks/idp-login.B596H5Zv.js +1 -0
- package/public/assets/chunks/theme.BUrgq2uM.js +1 -0
- package/public/assets/guides_getting-started.md.BMwF59kE.js +5 -0
- package/public/assets/guides_getting-started.md.BMwF59kE.lean.js +1 -0
- package/public/assets/guides_index.md.CUqoqPFW.js +1 -0
- package/public/assets/guides_index.md.CUqoqPFW.lean.js +1 -0
- package/public/assets/guides_openid-connect.md.CWezg52j.js +49 -0
- package/public/assets/guides_openid-connect.md.CWezg52j.lean.js +1 -0
- package/public/assets/guides_saml.md.BBlq_CTl.js +44 -0
- package/public/assets/guides_saml.md.BBlq_CTl.lean.js +1 -0
- package/public/assets/idp-login.B4Dj1tzS.png +0 -0
- package/public/assets/idp-saml-cert.Dyrxdyfk.png +0 -0
- package/public/assets/index.md.B8uyAhM4.js +1 -0
- package/public/assets/index.md.B8uyAhM4.lean.js +1 -0
- package/public/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/public/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/public/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/public/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/public/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/public/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/public/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/public/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/public/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/public/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/public/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/public/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/public/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/public/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/public/assets/introduction.md.DjcXFFe8.js +9 -0
- package/public/assets/introduction.md.DjcXFFe8.lean.js +1 -0
- package/public/assets/style.C4vbPc5Z.css +1 -0
- package/public/guides/getting-started.html +28 -0
- package/public/guides/index.html +24 -0
- package/public/guides/openid-connect.html +73 -0
- package/public/guides/saml.html +68 -0
- package/public/hashmap.json +1 -0
- package/public/index.html +24 -0
- package/public/introduction.html +32 -0
package/.editorconfig
ADDED
package/api/model/role.js
CHANGED
package/api/model/user.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module.exports = function() {
|
|
4
4
|
const api = this;
|
|
5
|
-
const { models,
|
|
5
|
+
const { models, service } = api;
|
|
6
6
|
|
|
7
|
-
const logAlert = api.log( "hitchy:
|
|
8
|
-
const logDebug = api.log( "hitchy:
|
|
7
|
+
const logAlert = api.log( "hitchy:auth:alert" );
|
|
8
|
+
const logDebug = api.log( "hitchy:auth:debug" );
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Implements policy handlers transparently managing authentication process
|
|
@@ -28,7 +28,7 @@ module.exports = function() {
|
|
|
28
28
|
|
|
29
29
|
this.local.passportInjected = true;
|
|
30
30
|
|
|
31
|
-
const { AuthenticationPassport } =
|
|
31
|
+
const { AuthenticationPassport } = service;
|
|
32
32
|
|
|
33
33
|
AuthenticationPassport.initialize()( req, res, initializeError => {
|
|
34
34
|
if ( initializeError ) {
|
|
@@ -42,7 +42,7 @@ module.exports = function() {
|
|
|
42
42
|
res.set( "X-Authorized-As", roles.join( "," ) );
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
next( sessionError );
|
|
45
|
+
this.updateSessionId( req, res, error => next( sessionError || error ) );
|
|
46
46
|
} );
|
|
47
47
|
}
|
|
48
48
|
} );
|
|
@@ -76,7 +76,7 @@ module.exports = function() {
|
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
service.AuthManager.checkAuthentication( parts[1], parts[2] )
|
|
80
80
|
.then( user => {
|
|
81
81
|
req.user = user; // eslint-disable-line no-param-reassign
|
|
82
82
|
|
|
@@ -96,7 +96,7 @@ module.exports = function() {
|
|
|
96
96
|
*/
|
|
97
97
|
static login( req, res, next ) {
|
|
98
98
|
const { strategy } = req.params;
|
|
99
|
-
const { AuthenticationStrategies, AuthenticationPassport } =
|
|
99
|
+
const { AuthenticationStrategies, AuthenticationPassport } = service;
|
|
100
100
|
const defaultStrategy = AuthenticationStrategies.defaultStrategy();
|
|
101
101
|
|
|
102
102
|
req.fetchBody()
|
|
@@ -119,7 +119,7 @@ module.exports = function() {
|
|
|
119
119
|
} );
|
|
120
120
|
} );
|
|
121
121
|
} )
|
|
122
|
-
.then( next )
|
|
122
|
+
.then( () => this.updateSessionId( req, res, next ) )
|
|
123
123
|
.catch( err => {
|
|
124
124
|
logAlert( err );
|
|
125
125
|
|
|
@@ -146,7 +146,7 @@ module.exports = function() {
|
|
|
146
146
|
if ( req.user ) {
|
|
147
147
|
const { uuid, name } = req.user;
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
service.AuthManager.listRolesOfUser( new models.User( uuid ) )
|
|
150
150
|
.then( roles => {
|
|
151
151
|
req.user.roles = roles; // eslint-disable-line no-param-reassign
|
|
152
152
|
|
|
@@ -192,21 +192,52 @@ module.exports = function() {
|
|
|
192
192
|
.then( async willLogoutInFuture => {
|
|
193
193
|
if ( !willLogoutInFuture ) {
|
|
194
194
|
if ( typeof req.logout === "function" ) {
|
|
195
|
-
await
|
|
195
|
+
await new Promise( ( resolve, reject ) => {
|
|
196
|
+
req.logout( error => ( error ? reject( error ) : resolve() ) );
|
|
197
|
+
} );
|
|
198
|
+
} else {
|
|
199
|
+
// re-generating session on change of user authentication mitigates session-related attacks
|
|
200
|
+
// -> passport's logout does not seem to exist, so we can't rely on passport regenerating the session
|
|
201
|
+
await new Promise( ( resolve, reject ) => req.session.regenerate( error => ( error ? reject( error ) : resolve() ) ) );
|
|
196
202
|
}
|
|
197
203
|
|
|
198
|
-
req.session.drop();
|
|
199
204
|
req.user = undefined; // eslint-disable-line no-param-reassign
|
|
200
205
|
|
|
201
206
|
res.set( "X-Authenticated-As", undefined );
|
|
202
207
|
res.set( "X-Authorized-As", undefined );
|
|
203
|
-
|
|
204
|
-
next();
|
|
205
208
|
}
|
|
209
|
+
|
|
210
|
+
this.updateSessionId( req, res, next );
|
|
206
211
|
} )
|
|
207
212
|
.catch( next );
|
|
208
213
|
}
|
|
209
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Promotes current session's ID in case it has changed compared to what
|
|
217
|
+
* the request was providing.
|
|
218
|
+
*
|
|
219
|
+
* @param {Hitchy.Core.IncomingMessage} req request descriptor
|
|
220
|
+
* @param {Hitchy.Core.ServerResponse} res response manager
|
|
221
|
+
* @param {Hitchy.Core.ContinuationHandler} next callback to invoke to continue processing a request
|
|
222
|
+
* @returns {void}
|
|
223
|
+
*/
|
|
224
|
+
static updateSessionId( req, res, next ) {
|
|
225
|
+
const { Session } = service;
|
|
226
|
+
|
|
227
|
+
const sid = req.session?.id;
|
|
228
|
+
|
|
229
|
+
if ( sid != null ) {
|
|
230
|
+
if ( sid !== Session.getSessionIdOfRequest( req ) ) {
|
|
231
|
+
// session might have been re-generated, hence its ID has
|
|
232
|
+
// changed and we need to inform the client to use a
|
|
233
|
+
// different session ID on next request
|
|
234
|
+
req.session.$promoteSessionIdInResponse( res );
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
next();
|
|
239
|
+
}
|
|
240
|
+
|
|
210
241
|
/**
|
|
211
242
|
* Ensures current request's user has authenticated.
|
|
212
243
|
*
|
package/api/policy/user.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module.exports = function() {
|
|
4
4
|
const api = this;
|
|
5
|
-
const { models, services } = api
|
|
5
|
+
const { models, services } = api;
|
|
6
6
|
|
|
7
|
-
const logDebug = api.log( "hitchy:
|
|
7
|
+
const logDebug = api.log( "hitchy:auth:debug" );
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Implements several helper methods meant to simplify user/role management.
|
|
@@ -5,8 +5,8 @@ const PassportLib = require( "passport" );
|
|
|
5
5
|
module.exports = function() {
|
|
6
6
|
const api = this;
|
|
7
7
|
|
|
8
|
-
const logAlert = api.log( "hitchy:
|
|
9
|
-
const logDebug = api.log( "hitchy:
|
|
8
|
+
const logAlert = api.log( "hitchy:auth:alert" );
|
|
9
|
+
const logDebug = api.log( "hitchy:auth:debug" );
|
|
10
10
|
|
|
11
11
|
const passport = new PassportLib.Passport();
|
|
12
12
|
|
|
@@ -16,7 +16,7 @@ module.exports = function() {
|
|
|
16
16
|
* @returns {void}
|
|
17
17
|
*/
|
|
18
18
|
passport.integrateWithHitchy = () => {
|
|
19
|
-
const { models: { User }, services: { AuthManager } } = api
|
|
19
|
+
const { models: { User }, services: { AuthManager } } = api;
|
|
20
20
|
const { strategies } = api.config.auth;
|
|
21
21
|
|
|
22
22
|
|
|
@@ -17,9 +17,9 @@ const RemoteAuthCustomData = new Map();
|
|
|
17
17
|
|
|
18
18
|
module.exports = function() {
|
|
19
19
|
const api = this;
|
|
20
|
-
const { models, services } = api
|
|
20
|
+
const { models, services } = api;
|
|
21
21
|
|
|
22
|
-
const logAlert = api.log( "hitchy:
|
|
22
|
+
const logAlert = api.log( "hitchy:auth:alert" );
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Fetches named user's local profile.
|
|
@@ -232,7 +232,7 @@ module.exports = function() {
|
|
|
232
232
|
getLocalProfile( strategyName, userInfo.preferred_username, true, done );
|
|
233
233
|
};
|
|
234
234
|
|
|
235
|
-
const { Issuer, Strategy } = require( "openid-client" );
|
|
235
|
+
const { Issuer, Strategy, generators } = require( "openid-client" );
|
|
236
236
|
const issuer = await Issuer.discover( config.discovery_url );
|
|
237
237
|
const client = new issuer.Client( config );
|
|
238
238
|
|
|
@@ -248,7 +248,7 @@ module.exports = function() {
|
|
|
248
248
|
return Promise.resolve( false );
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
const state = req.session[key] =
|
|
251
|
+
const state = req.session[key] = generators.state( 32 ); // eslint-disable-line no-param-reassign
|
|
252
252
|
|
|
253
253
|
// redirect user to discovered end_session_url of IdP
|
|
254
254
|
req.context.response.redirect( 302, client.endSessionUrl( { state } ) );
|
|
@@ -146,36 +146,44 @@ module.exports = function() {
|
|
|
146
146
|
* @returns {number} >0 on accepting user(s)/role(s) explicitly, <0 on rejecting explicitly, ==0 on missing explicit impact on user(s)/role(s)
|
|
147
147
|
*/
|
|
148
148
|
isAuthorized( userName, roleName ) {
|
|
149
|
+
const state = { accept: 0, reject: 0 };
|
|
150
|
+
|
|
151
|
+
if ( this.users.accept?.["*"] > 0 || this.roles.accept?.["*"] > 0 ) {
|
|
152
|
+
state.accept = 1;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if ( this.users.reject?.["*"] > 0 || this.roles.reject?.["*"] > 0 ) {
|
|
156
|
+
state.reject = 1;
|
|
157
|
+
}
|
|
158
|
+
|
|
149
159
|
const users = Array.isArray( userName ) ? userName : userName ? [userName] : [];
|
|
150
160
|
const roles = Array.isArray( roleName ) ? roleName : roleName ? [roleName] : [];
|
|
151
|
-
const actions = [ "accept", "reject" ];
|
|
152
|
-
const state = { accept: false, reject: false };
|
|
153
|
-
|
|
154
|
-
for ( const [ names, rules ] of [ [ users, this.users ], [ roles, this.roles ] ] ) {
|
|
155
|
-
for ( let i = 0, length = names.length; i < length; i++ ) {
|
|
156
|
-
const name = names[i];
|
|
157
161
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
for ( const [ names, rules, level ] of [ [ users, this.users, 3 ], [ roles, this.roles, 2 ] ] ) {
|
|
163
|
+
for ( const name of names ) {
|
|
164
|
+
if ( name && typeof name === "string" ) {
|
|
165
|
+
const _name = name.trim();
|
|
161
166
|
|
|
162
|
-
|
|
163
|
-
|
|
167
|
+
if ( _name !== "" ) {
|
|
168
|
+
for ( const action of [ "accept", "reject" ] ) {
|
|
169
|
+
if ( rules[action][_name] > 0 ) {
|
|
170
|
+
state[action] = Math.max( state[action], level );
|
|
171
|
+
}
|
|
164
172
|
}
|
|
165
173
|
}
|
|
166
174
|
}
|
|
167
175
|
}
|
|
168
176
|
}
|
|
169
177
|
|
|
170
|
-
if ( state.accept ) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
178
|
+
if ( state.accept > 0 && state.reject === state.accept ) {
|
|
179
|
+
throw new Error( `granting and revoking access on "${this.path()}" to/from user(s) "${userName || "<none>"}" and/or role(s) "${roleName || "<none>"}"` );
|
|
180
|
+
}
|
|
174
181
|
|
|
182
|
+
if ( state.accept > state.reject ) {
|
|
175
183
|
return 1;
|
|
176
184
|
}
|
|
177
185
|
|
|
178
|
-
if ( state.reject ) {
|
|
186
|
+
if ( state.accept < state.reject ) {
|
|
179
187
|
return -1;
|
|
180
188
|
}
|
|
181
189
|
|
|
@@ -183,7 +191,7 @@ module.exports = function() {
|
|
|
183
191
|
}
|
|
184
192
|
|
|
185
193
|
/**
|
|
186
|
-
* Indicates if current node
|
|
194
|
+
* Indicates if current node lacks any viable information.
|
|
187
195
|
*
|
|
188
196
|
* @returns {boolean} true if node hasn't any viable information affecting authorization checks
|
|
189
197
|
*/
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module.exports = function() {
|
|
4
4
|
const api = this;
|
|
5
|
-
const { services, models } = api
|
|
5
|
+
const { services, models } = api;
|
|
6
6
|
|
|
7
|
-
const logAlert = api.log( "hitchy:
|
|
8
|
-
const logDebug = api.log( "hitchy:
|
|
7
|
+
const logAlert = api.log( "hitchy:auth:alert" );
|
|
8
|
+
const logDebug = api.log( "hitchy:auth:debug" );
|
|
9
9
|
|
|
10
10
|
let cachedTree;
|
|
11
11
|
|
|
@@ -153,16 +153,16 @@ module.exports = function() {
|
|
|
153
153
|
*
|
|
154
154
|
* @param {string} selector selector of authorization, full-stop-separated sequence of names
|
|
155
155
|
* @param {string|string[]} user names(s) of zero or more users to check
|
|
156
|
-
* @param {string|string[]}
|
|
156
|
+
* @param {string|string[]} roles names of zero or more roles to check
|
|
157
157
|
* @param {boolean} acceptByDefault fallback result ro return if neither rule is affecting selected user(s) or role(s)
|
|
158
158
|
* @returns {boolean} true if authorization is granted to user(s)/role(s), false if it's rejected
|
|
159
159
|
*/
|
|
160
|
-
isAuthorized( selector, user,
|
|
160
|
+
isAuthorized( selector, user, roles, acceptByDefault = false ) {
|
|
161
161
|
let result = 0;
|
|
162
162
|
|
|
163
163
|
try {
|
|
164
164
|
this.selectNode( selector, false, node => {
|
|
165
|
-
const localResult = node.isAuthorized( user,
|
|
165
|
+
const localResult = node.isAuthorized( user, roles, acceptByDefault );
|
|
166
166
|
|
|
167
167
|
if ( localResult !== 0 ) {
|
|
168
168
|
result = localResult;
|
|
@@ -267,10 +267,12 @@ module.exports = function() {
|
|
|
267
267
|
continue;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
-
const list = String( items ).trim().split(
|
|
270
|
+
const list = String( items ).trim().split( "," )
|
|
271
|
+
.map( i => String( i ?? "" ).trim().replace( /([+-])\s*/, "$1" ) ) // remove whitespace here to mitigate ReDOS vulnerability in pattern below
|
|
272
|
+
.filter( i => i != null && i !== "" );
|
|
271
273
|
|
|
272
274
|
for ( const who of list ) {
|
|
273
|
-
const [ all, mode, role, id ] =
|
|
275
|
+
const [ all, mode, role, id ] = /^([+-]?)(@?)\s*([^\s@,+-][^\s@,]*)$/.exec( who ) || [];
|
|
274
276
|
|
|
275
277
|
if ( all ) {
|
|
276
278
|
const isGranting = mode ? mode !== "-" : key !== "revoke";
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = function( options, HitchyPluginSession ) {
|
|
4
|
+
const api = this;
|
|
5
|
+
|
|
6
|
+
const logDebug = api.log( "hitchy:auth:debug" );
|
|
7
|
+
const logError = api.log( "hitchy:auth:error" );
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extends session implemented by `@hitchy/plugin-session` to provide
|
|
11
|
+
* additional methods used by passport as it is assuming to work with
|
|
12
|
+
* `express-session`.
|
|
13
|
+
*/
|
|
14
|
+
class PassportCompatibleSession extends HitchyPluginSession {
|
|
15
|
+
#id;
|
|
16
|
+
|
|
17
|
+
#user;
|
|
18
|
+
|
|
19
|
+
#data;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} sessionId unique ID of session
|
|
23
|
+
* @param {Object<string,any>} properties copy (!) of session's properties recovered from a session store
|
|
24
|
+
*/
|
|
25
|
+
constructor( sessionId, properties ) {
|
|
26
|
+
super( sessionId, properties, false );
|
|
27
|
+
|
|
28
|
+
this.#id = sessionId;
|
|
29
|
+
this.#data = properties;
|
|
30
|
+
|
|
31
|
+
Object.defineProperties( this, {
|
|
32
|
+
/**
|
|
33
|
+
* Exposes ID of session.
|
|
34
|
+
*
|
|
35
|
+
* @name Session#id
|
|
36
|
+
* @property {string}
|
|
37
|
+
* @readonly
|
|
38
|
+
*/
|
|
39
|
+
id: { get: () => this.#id },
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Exposes authenticated user current session is associated with.
|
|
43
|
+
*
|
|
44
|
+
* @name Session#user
|
|
45
|
+
* @property {{uuid:string}}
|
|
46
|
+
*/
|
|
47
|
+
user: {
|
|
48
|
+
get: () => this.#user,
|
|
49
|
+
set: newUser => {
|
|
50
|
+
if ( !newUser || typeof newUser !== "object" || !newUser.uuid ) {
|
|
51
|
+
throw new Error( "invalid user descriptor rejected" );
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if ( this.#user != null ) {
|
|
55
|
+
if ( newUser.uuid === this.#user.uuid ) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
throw new Error( "invalid request for replacing user of current session" );
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.#user = Object.freeze( { ...newUser } );
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Exposes space for saving additional custom data in context of
|
|
68
|
+
* current session.
|
|
69
|
+
*
|
|
70
|
+
* @name Session#data
|
|
71
|
+
* @property {object}
|
|
72
|
+
* @readonly
|
|
73
|
+
*/
|
|
74
|
+
$data: { get: () => this.#data },
|
|
75
|
+
} );
|
|
76
|
+
|
|
77
|
+
// OIDC plugin for passport is using custom redirection that's
|
|
78
|
+
// preventing session from being persisted in late policy
|
|
79
|
+
// -> instantly persist any changes related to the OIDC integration
|
|
80
|
+
this.on( "data-changed", ( [key] ) => {
|
|
81
|
+
if ( key.startsWith( "oidc:" ) ) {
|
|
82
|
+
logDebug( "instantly persisting session on setting %s", key );
|
|
83
|
+
|
|
84
|
+
this.constructor.getStore().save( this.id, this.$data ).catch( cause => {
|
|
85
|
+
logError( "persisting session failed:", cause.stack );
|
|
86
|
+
} );
|
|
87
|
+
}
|
|
88
|
+
} );
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Drops any current session and creates a new one.
|
|
93
|
+
*
|
|
94
|
+
* This method is used by passport.
|
|
95
|
+
*
|
|
96
|
+
* @param {function(error: (Error|undefined)): void} doneFn callback invoked when session has been re-generated or some error occurred
|
|
97
|
+
* @returns {void}
|
|
98
|
+
*/
|
|
99
|
+
regenerate( doneFn ) {
|
|
100
|
+
logDebug( "re-generating current user session" );
|
|
101
|
+
|
|
102
|
+
const store = this.constructor.getStore();
|
|
103
|
+
|
|
104
|
+
store.drop( this.#id )
|
|
105
|
+
.then( () => store.create() )
|
|
106
|
+
.then( sessionId => {
|
|
107
|
+
if ( !sessionId ) {
|
|
108
|
+
throw new Error( "failed to assign new session ID" );
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return store.load( sessionId )
|
|
112
|
+
.then( record => {
|
|
113
|
+
if ( !record ) {
|
|
114
|
+
throw new Error( "failed to recover new session record" );
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.#id = sessionId;
|
|
118
|
+
this.#data = record;
|
|
119
|
+
this.#user = null;
|
|
120
|
+
|
|
121
|
+
logDebug( `new session ID is ${sessionId}` );
|
|
122
|
+
doneFn();
|
|
123
|
+
} );
|
|
124
|
+
} )
|
|
125
|
+
.catch( cause => {
|
|
126
|
+
logError( "re-generating current user session failed:", cause.stack );
|
|
127
|
+
|
|
128
|
+
doneFn( cause );
|
|
129
|
+
} );
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Explicitly triggers saving of current session to configured store.
|
|
134
|
+
*
|
|
135
|
+
* This method is used by passport.
|
|
136
|
+
*
|
|
137
|
+
* @param {function(error: (Error|undefined)): void} doneFn callback invoked when session has been saved or some error occurred
|
|
138
|
+
* @returns {void}
|
|
139
|
+
*/
|
|
140
|
+
save( doneFn ) {
|
|
141
|
+
const store = this.constructor.getStore();
|
|
142
|
+
|
|
143
|
+
store.save( this.#id, this.#data )
|
|
144
|
+
.then( () => doneFn() )
|
|
145
|
+
.catch( doneFn );
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return PassportCompatibleSession;
|
|
150
|
+
};
|
package/index.js
CHANGED
|
@@ -10,7 +10,7 @@ module.exports = function( options, plugins ) {
|
|
|
10
10
|
const { strategies } = api.config.auth;
|
|
11
11
|
|
|
12
12
|
if ( !strategies.local ) {
|
|
13
|
-
strategies.local = api.
|
|
13
|
+
strategies.local = api.services.AuthenticationStrategies.generateLocal();
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
|
|
@@ -22,7 +22,7 @@ module.exports = function( options, plugins ) {
|
|
|
22
22
|
async initialize() {
|
|
23
23
|
const {
|
|
24
24
|
services: { AuthorizationTree, AuthenticationPassport, AuthManager },
|
|
25
|
-
} = api
|
|
25
|
+
} = api;
|
|
26
26
|
const { current } = AuthorizationTree;
|
|
27
27
|
|
|
28
28
|
AuthenticationPassport.integrateWithHitchy();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hitchy/plugin-auth",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "user authentication and authorization for Hitchy",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"lint": "eslint .",
|
|
9
9
|
"test": "hitchy-pm session cookies odem --exec c8 -r text -r html mocha --recursive --exclude **/*.dist.js ./test/scripts --ui bdd",
|
|
10
10
|
"test:smoke": "hitchy-pm session cookies odem --exec hitchy --project test/project/testbed --plugins . --plugin . --debug",
|
|
11
|
-
"docs:dev": "
|
|
12
|
-
"docs:build": "
|
|
11
|
+
"docs:dev": "vitepress dev docs",
|
|
12
|
+
"docs:build": "vitepress build docs"
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
@@ -25,24 +25,24 @@
|
|
|
25
25
|
"@hitchy/core": "0.8.x"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@hitchy/server-dev-tools": "^0.4.
|
|
28
|
+
"@hitchy/server-dev-tools": "^0.4.9",
|
|
29
29
|
"@hitchy/types": "^0.1.3",
|
|
30
|
-
"c8": "^
|
|
31
|
-
"eslint": "^8.
|
|
32
|
-
"eslint-config-cepharum": "^1.0.
|
|
30
|
+
"c8": "^10.1.2",
|
|
31
|
+
"eslint": "^8.57.0",
|
|
32
|
+
"eslint-config-cepharum": "^1.0.14",
|
|
33
33
|
"eslint-plugin-promise": "^6.1.1",
|
|
34
|
-
"mocha": "^10.
|
|
35
|
-
"openid-client": "^5.
|
|
34
|
+
"mocha": "^10.6.0",
|
|
35
|
+
"openid-client": "^5.6.5",
|
|
36
36
|
"passport-saml": "^3.2.4",
|
|
37
37
|
"should": "^13.2.3",
|
|
38
38
|
"should-http": "^0.1.1",
|
|
39
|
-
"
|
|
39
|
+
"vitepress": "^1.3.1"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@hitchy/plugin-cookies": "^0.1.8",
|
|
43
43
|
"@hitchy/plugin-odem": "^0.7.8",
|
|
44
|
-
"@hitchy/plugin-session": "^0.
|
|
45
|
-
"passport": "^0.
|
|
44
|
+
"@hitchy/plugin-session": "^0.4.0",
|
|
45
|
+
"passport": "^0.7.0",
|
|
46
46
|
"passport-local": "^1.0.0"
|
|
47
47
|
}
|
|
48
48
|
}
|
package/public/404.html
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-US" dir="ltr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>404 | Hitchy Auth Manual</title>
|
|
7
|
+
<meta name="description" content="Not Found">
|
|
8
|
+
<meta name="generator" content="VitePress v1.3.1">
|
|
9
|
+
<link rel="preload stylesheet" href="/assets/style.C4vbPc5Z.css" as="style">
|
|
10
|
+
|
|
11
|
+
<script type="module" src="/assets/app.Bnek3cfe.js"></script>
|
|
12
|
+
<link rel="preload" href="/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
|
|
13
|
+
<script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
|
|
14
|
+
<script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
<div id="app"></div>
|
|
18
|
+
<script>window.__VP_HASH_MAP__=JSON.parse("{\"api_config.md\":\"BiPnBhyk\",\"api_controller_index.md\":\"mhiyhr_C\",\"api_controller_user.md\":\"BiFYPTow\",\"api_index.md\":\"j6eBaebO\",\"api_model_authorization-rule.md\":\"CFNqudsp\",\"api_model_index.md\":\"Dw3UH73J\",\"api_model_role.md\":\"DFCGXTBA\",\"api_model_user-to-role.md\":\"QNC96rs-\",\"api_model_user.md\":\"C2GSzwZj\",\"api_policy_authentication.md\":\"Ccj8Rneb\",\"api_policy_authorization.md\":\"CP3y7VOT\",\"api_policy_index.md\":\"CmaeRtru\",\"api_policy_user.md\":\"ePU_LHGT\",\"api_routing.md\":\"BP98xeNw\",\"api_service_auth-manager.md\":\"CcpV6slZ\",\"api_service_authentication-passport.md\":\"DvhoW1TR\",\"api_service_authentication-strategies.md\":\"DjDT2F9g\",\"api_service_authorization-node.md\":\"DAN4WdDZ\",\"api_service_authorization-policy-generator.md\":\"IaQjgxfZ\",\"api_service_authorization-tree.md\":\"I7ff4vao\",\"api_service_index.md\":\"Bfk1E4Zn\",\"guides_getting-started.md\":\"BMwF59kE\",\"guides_index.md\":\"CUqoqPFW\",\"guides_openid-connect.md\":\"CWezg52j\",\"guides_saml.md\":\"BBlq_CTl\",\"index.md\":\"B8uyAhM4\",\"introduction.md\":\"DjcXFFe8\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Hitchy Auth Manual\",\"description\":\"A VitePress site\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"displayAllHeaders\":true,\"socialLinks\":[{\"icon\":{\"svg\":\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 512 512\\\"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d=\\\"M503.5 204.6L502.8 202.8L433.1 21C431.7 17.5 429.2 14.4 425.9 12.4C423.5 10.8 420.8 9.9 417.9 9.6C415 9.3 412.2 9.7 409.5 10.7C406.8 11.7 404.4 13.3 402.4 15.5C400.5 17.6 399.1 20.1 398.3 22.9L351.3 166.9H160.8L113.7 22.9C112.9 20.1 111.5 17.6 109.6 15.5C107.6 13.4 105.2 11.7 102.5 10.7C99.9 9.7 97 9.3 94.1 9.6C91.3 9.9 88.5 10.8 86.1 12.4C82.8 14.4 80.3 17.5 78.9 21L9.3 202.8L8.5 204.6C-1.5 230.8-2.7 259.6 5 286.6C12.8 313.5 29.1 337.3 51.5 354.2L51.7 354.4L52.3 354.8L158.3 434.3L210.9 474L242.9 498.2C246.6 500.1 251.2 502.5 255.9 502.5C260.6 502.5 265.2 500.1 268.9 498.2L300.9 474L353.5 434.3L460.2 354.4L460.5 354.1C482.9 337.2 499.2 313.5 506.1 286.6C514.7 259.6 513.5 230.8 503.5 204.6z\\\"/></svg>\"},\"link\":\"https://gitlab.com/hitchy/plugin-auth\",\"ariaLabel\":\"Link to code repository\"}],\"nav\":[{\"text\":\"Home\",\"link\":\"/\"},{\"text\":\"Guides\",\"link\":\"/guides/\"},{\"text\":\"API\",\"link\":\"/api/\"},{\"text\":\"Hitchy\",\"items\":[{\"text\":\"Core\",\"link\":\"https://core.hitchy.org/\"},{\"text\":\"Plugins\",\"items\":[{\"text\":\"Odem\",\"link\":\"https://odem.hitchy.org/\"},{\"text\":\"Auth\",\"link\":\"/\"}]},{\"text\":\"Tools\",\"items\":[{\"text\":\"SDT\",\"link\":\"https://sdt.hitchy.org/\"}]}]}]},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":false}");</script>
|
|
19
|
+
|
|
20
|
+
</body>
|
|
21
|
+
</html>
|