Skip to content

Commit

Permalink
migration: completed Meteor 3.0 migration
Browse files Browse the repository at this point in the history
  • Loading branch information
jankapunkt committed Aug 1, 2024
1 parent e309ad8 commit c0f72e3
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 36 deletions.
5 changes: 3 additions & 2 deletions .meteor/packages
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ force-ssl@1.1.1
# TESTING
#========================================
leaonline:testing
# lmieulet:meteor-coverage@3.2.0
meteortesting:mocha@3.0.0-rc.1
lmieulet:meteor-coverage@4.3.0
lmieulet:meteor-legacy-coverage@0.4.0
meteortesting:mocha@3.2.0
hwillson:stub-collections@2.0.0-rc.0

# leaonline:status
8 changes: 5 additions & 3 deletions .meteor/versions
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
accounts-base@3.0.0
accounts-password@3.0.0
alanning:roles@4.0.0-rc.1
aldeed:autoform@8.0.0-rc.3
aldeed:autoform@8.0.0-rc.4
aldeed:simple-schema@2.0.0-rc300.1
allow-deny@2.0.0
audit-argument-checks@1.0.8
Expand Down Expand Up @@ -51,17 +51,19 @@ jkuester:autoform-password2@3.0.0-rc.0
jquery@3.0.0
leaonline:method-factory@2.0.0
leaonline:oauth2-server@6.0.0-rc.0
leaonline:ratelimit-factory@1.0.0
leaonline:ratelimit-factory@2.0.0
leaonline:testing@2.0.0
leaonline:theme@2.0.0
leaonline:utils@2.0.0
lmieulet:meteor-coverage@4.3.0
lmieulet:meteor-legacy-coverage@0.4.0
localstorage@1.2.1
logging@1.3.5
mdg:validated-method@1.3.0
meteor@2.0.0
meteor-base@1.5.2
meteortesting:browser-tests@1.7.0
meteortesting:mocha@3.0.0-rc.1
meteortesting:mocha@3.0.0
meteortesting:mocha-core@8.2.0
minifier-js@3.0.0
minimongo@2.0.0
Expand Down
14 changes: 13 additions & 1 deletion imports/api/accounts/canUserAccessClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ import { Meteor } from 'meteor/meteor'
import { Roles } from 'meteor/alanning:roles'
import { OAuth } from '../oauth/OAuth'

/**
* Determines, whether a given user is allowed to access
* one of the lea. applications.
* @param user {object}
* @param user._id {string}
* @param user.institution {string?}
* @param client {object}
* @param client.clientId {string}
* @return {Promise<boolean>}
*/
export const canUserAccessClient = async ({ user, client }) => {
if (!user?._id || !client?.clientId) {
return false
Expand All @@ -10,10 +20,12 @@ export const canUserAccessClient = async ({ user, client }) => {
const { clientId } = client
const { institution } = user
const userId = user._id
const clientKey = await OAuth.getClientKey(clientId)
const clientKey = OAuth.getClientKey(clientId)
const isInRole = await Roles.userIsInRoleAsync(userId, clientKey, institution)

if (!isInRole) {
// in this case we revoke the token to
// reduce the chance of access
await Meteor.users.updateAsync({ _id: userId }, { $set: { 'services.resume.loginTokens': [] } })
return false
}
Expand Down
40 changes: 36 additions & 4 deletions imports/api/oauth/OAuth.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,48 @@
import { Meteor } from 'meteor/meteor'

/**
* In-memory registry for OAuth workflow.
*/
export const OAuth = {}

/** @private */
const settings = Meteor.settings.oauth.clients
/** @private */
const clients = new Map()

settings.forEach(entry => {
clients.set(entry.clientId, entry.key)
})

export const OAuth = {}

OAuth.getClientKey = clientId => clients.get(clientId)
/**
* Returns the "primary" for a client by clientId.
* This key is usually not stored in the OAuth clients
* collection.
* @param clientId {string}
* @return {string|undefined}
*/
const getClientKey = clientId => clients.get(clientId)

OAuth.getIdentity = userId => {
const user = userId && Meteor.users.findOne(userId)
/**
* Returns the "identity" of a user by given userId.
* Note, the returned document is stripped from sensitive
* fields like {services}.
*
* @async
* @param userId {string}
* @return {Promise<{
* firstName:string,
* lastName:string,
* roles: string[],
* name: string,
* id:string,
* login:string?,
* email: string
* }>}
*/
const getIdentity = async userId => {
const user = userId && await Meteor.users.findOneAsync(userId)
if (!user) return

return {
Expand All @@ -25,3 +55,5 @@ OAuth.getIdentity = userId => {
roles: [].concat(user.roles || [])
}
}

Object.assign(OAuth, { getClientKey, getIdentity })
20 changes: 10 additions & 10 deletions imports/api/oauth/OAuth.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ describe('OAuth', function () {
afterEach(function () {
restoreAll()
})
it('returns undefined if no user is found', function () {
stub(Meteor.users, 'findOne', () => {})
expect(OAuth.getIdentity()).to.equal(undefined)
expect(OAuth.getIdentity(Random.id())).to.equal(undefined)
it('returns undefined if no user is found', async function () {
stub(Meteor.users, 'findOneAsync', async () => {})
expect(await OAuth.getIdentity()).to.equal(undefined)
expect(await OAuth.getIdentity(Random.id())).to.equal(undefined)

overrideStub(Meteor.users, 'findOne', () => {})
expect(OAuth.getIdentity()).to.equal(undefined)
expect(OAuth.getIdentity(Random.id())).to.equal(undefined)
overrideStub(Meteor.users, 'findOneAsync', async () => {})
expect(await OAuth.getIdentity()).to.equal(undefined)
expect(await OAuth.getIdentity(Random.id())).to.equal(undefined)
})
it('returns a valid identity', function () {
it('returns a valid identity', async function () {
const user = {
_id: Random.id(6),
username: Random.id(6),
Expand All @@ -38,8 +38,8 @@ describe('OAuth', function () {
emails: [{ address: [Random.id(6)] }],
roles: ['foo', 'bar']
}
stub(Meteor.users, 'findOne', () => user)
const result = OAuth.getIdentity(Random.id())
stub(Meteor.users, 'findOneAsync', () => user)
const result = await OAuth.getIdentity(Random.id())
expect(result).to.deep.equal({
id: user._id,
login: user.username,
Expand Down
24 changes: 10 additions & 14 deletions imports/startup/server/oauth.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,29 @@ const oauth2server = new OAuth2Server({
accessTokensCollection: new Mongo.Collection(null),
refreshTokensCollection: new Mongo.Collection(null),
clientsCollectionName: model.clientsCollectionName,
authCodesCollection: new Mongo.Collection(null)
authCodesCollection: new Mongo.Collection(null),
debug: false
},
routes,
debug
debug: true
})

oauth2server.validateUser(function (userData) {
return canUserAccessClient(userData)
})

oauth2server.authenticatedRoute().get(routes.identityUrl, function (req, res) {
oauth2server.authenticatedRoute().get(routes.identityUrl, async function (req, res) {
const userId = req?.data?.user?.id
const user = OAuth.getIdentity(userId)
const status = user ? 200 : 404
const user = await OAuth.getIdentity(userId)

res.writeHead(status, {
'Content-Type': 'application/json'
})
res.status(user ? 200 : 404)
res.set({ 'Content-Type': 'application/json' })

const body = user
? JSON.stringify(user)
: JSON.stringify({
error: 'user not found',
response: `request user [${userId}] not found`
})
? user
: { error: 'user not found', response: `request user [${userId}] not found` }

res.end(body)
res.json(body)
})

Meteor.startup(() => {
Expand Down
4 changes: 2 additions & 2 deletions test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ METEOR_PACKAGE_DIRS=${T_PACKAGE_DIRS} \
BABEL_ENV=COVERAGE \
COVERAGE=${T_COVERAGE} \
COVERAGE_OUT_HTML=1 \
COVERAGE_OUT_LCOVONLY=1 \
COVERAGE_OUT_TEXT_SUMMARY=1 \
COVERAGE_APP_FOLDER=$PWD/ \
COVERAGE_VERBOSE_MODE=${T_VERBOSE} \
COVERAGE_VERBOSE=${T_VERBOSE} \
meteor test \
${T_RUN_ONCE} \
--driver-package=meteortesting:mocha \
Expand Down

0 comments on commit c0f72e3

Please sign in to comment.