This repository has been archived by the owner on Apr 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #466 from dylanmcreynolds/feature/oidc
Enhance server.js to load passport loginCallbacks . Thanks for the effort !
- Loading branch information
Showing
8 changed files
with
324 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,28 @@ | ||
# gives a docker image below 200 MB | ||
FROM mhart/alpine-node:10 | ||
FROM node:14-alpine | ||
RUN apk update && apk upgrade | ||
|
||
RUN apk add --update python build-base git | ||
ENV NODE_ENV "production" | ||
ENV PORT 3000 | ||
EXPOSE 3000 | ||
# create local user to avoid running as root | ||
RUN addgroup mygroup && adduser -D -G mygroup myuser && mkdir -p /usr/src/app && chown -R myuser /usr/src/app | ||
|
||
# Prepare app directory | ||
WORKDIR /usr/src/app | ||
COPY package*.json ./ | ||
COPY .snyk ./ | ||
WORKDIR /home/node/app | ||
COPY package*.json /home/node/app/ | ||
COPY .snyk /home/node/app/ | ||
|
||
USER myuser | ||
|
||
RUN npm install | ||
# set up local user to avoid running as root | ||
# RUN chown -R node:node /home/node/app | ||
# USER node | ||
|
||
# Install our packages | ||
RUN npm ci --production | ||
RUN npm config set registry http://registry.npmjs.org/ | ||
RUN npm config set strict-ssl false | ||
RUN npm ci --only=production | ||
|
||
# Copy the rest of our application, node_modules is ignored via .dockerignore | ||
COPY . /usr/src/app | ||
COPY . /home/node/app | ||
|
||
|
||
# Start the app | ||
CMD ["node", "."] | ||
CMD ["node", "."] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"mongo": { | ||
"host": "mongodb", | ||
"port": 27017, | ||
"url": "", | ||
"database": "dacat", | ||
"name": "mongo", | ||
"connector": "mongodb", | ||
"useNewUrlParser": true, | ||
"allowExtendedOperators":true | ||
}, | ||
"transient": { | ||
"name": "transient", | ||
"connector": "transient" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
[{ | ||
"account": "admin", | ||
"password": "2jf0TPNZsS", | ||
"email": "[email protected]", | ||
"role": "admin", | ||
"global": true | ||
}, { | ||
"account": "ingestor", | ||
"password": "aman", | ||
"email": "[email protected]", | ||
"role": "ingestor", | ||
"global": true | ||
}, { | ||
"account": "archiveManager", | ||
"password": "aman", | ||
"email": "[email protected]", | ||
"role": "archivemanager" | ||
|
||
}, { | ||
"account": "proposalIngestor", | ||
"password": "aman", | ||
"email": "[email protected]", | ||
"role": "proposalingestor" | ||
}, { | ||
"account": "synmx", | ||
"password": "synmx", | ||
"email": "[email protected]", | ||
"role": "ingestor" | ||
}, { | ||
"account": "syncsaxs", | ||
"password": "syncsaxs", | ||
"email": "[email protected]", | ||
"role": "ingestor" | ||
}, { | ||
"account": "syntomcat", | ||
"password": "syntomcat", | ||
"email": "[email protected]", | ||
"role": "ingestor" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
"use strict"; | ||
|
||
/* | ||
This module communicates with ALSHub at two different points in the login process. | ||
The first is an observer on the UserIdentity model that adds group information to the user's | ||
profile from ALSHub. This gets used by Catamel to enforce access controls. | ||
The second is a loginCallback for passport that checks that the user is a user in ALSHub. | ||
If not, it sends blank credentials to the return of the loginCallback, prompting | ||
loopback-passport to not log the user in. | ||
*/ | ||
|
||
|
||
|
||
|
||
|
||
/* | ||
A loginCallback is function used by passport that gives one a chance | ||
to intercept the login message flow. This is useful in cases where the | ||
an OAuth2/OIDC provider is a third part (like ORCID), but an internal system | ||
must be queried to add information to the user's profile. | ||
With a generic passport implementation, one could simple attach the callback | ||
funtion as a member of the passport configuration. However, Catamel uses the | ||
loopback-passport-confgigurator, which is configed via a .json file (providers.json) | ||
and not through a .js file. This means that the entry in the is at best a string, not a | ||
function. | ||
server.js will | ||
- import this file | ||
- read the loginCallback configuration | ||
- if the string matches a function defined in this file, it will attach that function | ||
to the provider. | ||
Below is an example of a callback function. This is example mimics the default that passport | ||
configures if none has been configured. Note a few important things: | ||
- the function is exported through module.exports | ||
- the function calls done(err, user, authInfo) | ||
- if a custom call back decides that a user should not be logged in, call done(none, none, none) | ||
module.exports.sampleLoginCallback = function(req, done) { | ||
return function(err, user, identity, token) { | ||
var authInfo = { | ||
identity: identity, | ||
}; | ||
if (token) { | ||
authInfo.accessToken = token; | ||
} | ||
done(err, user, authInfo); | ||
}; | ||
}; | ||
*/ | ||
|
||
const logger = require("../../common/logger"); | ||
var request = require("request"); | ||
|
||
const authenticators = { | ||
ORCID: "orcid", | ||
GOOGLE: "google" | ||
}; | ||
|
||
const reqIDFields = { | ||
ORCID: "orcid", | ||
EMAIL: "email" | ||
}; | ||
|
||
function getUserURL(userIdentity){ | ||
let idField = ""; | ||
let subjectId = ""; | ||
if (userIdentity.provider == authenticators.ORCID){ | ||
idField = reqIDFields.ORCID; | ||
subjectId = userIdentity.profile._json.sub; | ||
} | ||
else if (userIdentity.provider == authenticators.GOOGLE){ | ||
idField = reqIDFields.EMAIL; | ||
subjectId = userIdentity.profile._json.email; | ||
} | ||
else{ | ||
return null; | ||
} | ||
return `${process.env.USER_SVC_API_URL}${subjectId}/${idField}?api_key=${process.env.USER_SVC_API_KEY}`; | ||
} | ||
|
||
// Observe saving UserItentity. This gives us the ability to update the user profile with facitly-specific groups | ||
module.exports = function (app) { | ||
app.models.UserIdentity.observe("before save", function(ctx, next) { | ||
if (!ctx.data){ | ||
logger.logInfo("No context data from UserItentity"); | ||
next(); | ||
return; | ||
} | ||
const userURL = getUserURL(ctx.currentInstance); | ||
if (!userURL){ | ||
logger.logError(`unexpected authenticator type: ${ctx.currentInstance.provider}`); | ||
next(); | ||
return; | ||
} | ||
request(userURL, function (error, response, _body) { | ||
// ask ALSHub for user information so we can get group info | ||
if (error){ | ||
logger.logError(`error talking to splash_userservice ${error.message}`); | ||
next(); | ||
return; | ||
} | ||
if (response.statusCode == 200){ | ||
// add groups to profile, saving in the UserItentity model | ||
ctx.data.profile.accessGroups = JSON.parse(response.body).groups; | ||
} | ||
next(); | ||
}); | ||
}); | ||
}; | ||
|
||
|
||
// ALS Login callback, registered with Passport. This gives us the opportunity | ||
// to deny login if the user is not found in the facility directory. | ||
module.exports.alsLoginCallback = function(req, done) { | ||
return function(err, user, identity, token) { | ||
|
||
var authInfo = { | ||
identity: identity, | ||
}; | ||
if (token) { | ||
authInfo.accessToken = token; | ||
} | ||
|
||
const requestURL = getUserURL(identity); | ||
if (!requestURL){ | ||
logger.logError(`unexpected authenticator type: ${identity.provider}`); | ||
done(); | ||
return; | ||
} | ||
request(requestURL, function (error, response, body) { | ||
// ask ALSHub for the user's information | ||
if (error){ | ||
logger.logError(`error talking to splash_userservice ${error.message}`); | ||
done(err, null, null); | ||
return; | ||
} | ||
if (response.statusCode == 200) { | ||
const bodyObj = JSON.parse(body); | ||
logger.logInfo("user service returned", bodyObj); | ||
} | ||
else{ | ||
logger.logError(`error returned from splash_userservice ${response.statusCode} - ${body}.`); | ||
// user couldn't be found, prevent login by sending nulls | ||
done(err, null, null); | ||
return; | ||
} | ||
done(err, user, authInfo); | ||
}); | ||
}; | ||
}; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
A loginCallback is function used by passport that gives one a chance | ||
to intercept the login message flow. This is useful in cases where the | ||
an OAuth2/OIDC provider is a third part (like ORCID), but an internal system | ||
must be queried to add information to the user's profile. | ||
With a generic passport implementation, one could simple attach the callback | ||
funtion as a member of the passport configuration. However, Catamel uses the | ||
loopback-passport-confgigurator, which is configed via a .json file (providers.json) | ||
and not through a .js file. This means that the entry in the is at best a string, not a | ||
function. | ||
server.js will | ||
- import this file | ||
- read the loginCallback configuration | ||
- if the string matches a function defined in this file, it will attach that function | ||
to the provider. | ||
Below is an example of a callback function. This is example mimics the default that passport | ||
configures if none has been configured. Note a few important things: | ||
- the function is exported through module.exports | ||
- the function calls done(err, user, authInfo) | ||
- if a custom call back decides that a user should not be logged in, call done(none, none, none) | ||
module.exports.sampleLoginCallback = function(req, done) { | ||
return function(err, user, identity, token) { | ||
var authInfo = { | ||
identity: identity, | ||
}; | ||
if (token) { | ||
authInfo.accessToken = token; | ||
} | ||
done(err, user, authInfo); | ||
}; | ||
}; | ||
*/ |
Oops, something went wrong.