mirror of https://github.com/status-im/codimd.git
feat: add organizations whitelist to GitHub OAuth
Currently CodiMD does not support limiting access of GitHub OAuth users based on their organization membership. This is a very useful functionality for teams that want to limit write access to their notes. I've implemented a crude solution to this problem, which most probably requires some adjusments to make it better. I'm not sure if this implementation is kosher, but it definitely works on my deployment. Open to suggestions on how I can improve it. Signed-off-by: Jakub Sokołowski <jakub@status.im>
This commit is contained in:
parent
3b1e270952
commit
0d1cd1d2f0
8
app.json
8
app.json
|
@ -80,6 +80,14 @@
|
|||
"description": "GitHub API client secret",
|
||||
"required": false
|
||||
},
|
||||
"CMD_GITHUB_ORGANIZATIONS": {
|
||||
"description": "GitHub whitelist of orgs",
|
||||
"required": false
|
||||
},
|
||||
"CMD_GITHUB_SCOPES": {
|
||||
"description": "GitHub OAuth API scopes",
|
||||
"required": false
|
||||
},
|
||||
"CMD_BITBUCKET_CLIENTID": {
|
||||
"description": "Bitbucket API client id",
|
||||
"required": false
|
||||
|
|
|
@ -54,6 +54,8 @@
|
|||
"github": {
|
||||
"clientID": "change this",
|
||||
"clientSecret": "change this"
|
||||
"organizations": ["names of github organizations allowed, optional"],
|
||||
"scopes": ["defaults to 'read:user' scope for auth user"],
|
||||
},
|
||||
"gitlab": {
|
||||
"baseURL": "change this",
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
'use strict'
|
||||
|
||||
const Router = require('express').Router
|
||||
const request = require('request')
|
||||
const passport = require('passport')
|
||||
const GithubStrategy = require('passport-github').Strategy
|
||||
const { InternalOAuthError } = require('passport-oauth2')
|
||||
const config = require('../../config')
|
||||
const response = require('../../response')
|
||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||
const { URL } = require('url')
|
||||
const { promisify } = require('util')
|
||||
|
||||
const rp = promisify(request)
|
||||
|
||||
const githubAuth = module.exports = Router()
|
||||
|
||||
|
@ -15,20 +20,47 @@ function githubUrl (path) {
|
|||
}
|
||||
|
||||
passport.use(new GithubStrategy({
|
||||
scope: (config.github.organizations ?
|
||||
config.github.scopes.concat(['read:org']) : config.github.scope),
|
||||
clientID: config.github.clientID,
|
||||
clientSecret: config.github.clientSecret,
|
||||
callbackURL: config.serverURL + '/auth/github/callback',
|
||||
authorizationURL: githubUrl('login/oauth/authorize'),
|
||||
tokenURL: githubUrl('login/oauth/access_token'),
|
||||
userProfileURL: githubUrl('api/v3/user')
|
||||
}, passportGeneralCallback))
|
||||
}, async (accessToken, refreshToken, profile, done) => {
|
||||
if (!config.github.organizations) {
|
||||
return passportGeneralCallback(accessToken, refreshToken, profile, done)
|
||||
}
|
||||
const { statusCode, body: data } = await rp({
|
||||
url: `https://api.github.com/user/orgs`,
|
||||
method: 'GET', json: true, timeout: 2000,
|
||||
headers: {
|
||||
'Authorization': `token ${accessToken}`,
|
||||
'User-Agent': 'nodejs-http'
|
||||
}
|
||||
})
|
||||
if (statusCode != 200) {
|
||||
return done(InternalOAuthError(
|
||||
`Failed to query organizations for user: ${profile.username}`
|
||||
))
|
||||
}
|
||||
const orgs = data.map(({login}) => login)
|
||||
for (const org of orgs) {
|
||||
if (config.github.organizations.includes(org)) {
|
||||
return passportGeneralCallback(accessToken, refreshToken, profile, done)
|
||||
}
|
||||
}
|
||||
return done(InternalOAuthError(
|
||||
`User orgs not whitelisted: ${profile.username} (${orgs.join(',')})`
|
||||
))
|
||||
}))
|
||||
|
||||
githubAuth.get('/auth/github', function (req, res, next) {
|
||||
setReturnToFromReferer(req)
|
||||
passport.authenticate('github')(req, res, next)
|
||||
})
|
||||
|
||||
// github auth callback
|
||||
githubAuth.get('/auth/github/callback',
|
||||
passport.authenticate('github', {
|
||||
successReturnToOrRedirect: config.serverURL + '/',
|
||||
|
|
|
@ -115,7 +115,9 @@ module.exports = {
|
|||
github: {
|
||||
enterpriseURL: undefined, // if you use github.com, not need to specify
|
||||
clientID: undefined,
|
||||
clientSecret: undefined
|
||||
clientSecret: undefined,
|
||||
organizations: [],
|
||||
scopes: ['read:user']
|
||||
},
|
||||
gitlab: {
|
||||
baseURL: undefined,
|
||||
|
|
|
@ -69,7 +69,9 @@ module.exports = {
|
|||
github: {
|
||||
enterpriseURL: process.env.CMD_GITHUB_ENTERPRISE_URL,
|
||||
clientID: process.env.CMD_GITHUB_CLIENTID,
|
||||
clientSecret: process.env.CMD_GITHUB_CLIENTSECRET
|
||||
clientSecret: process.env.CMD_GITHUB_CLIENTSECRET,
|
||||
organizations: toArrayConfig(process.env.CMD_GITHUB_ORGANIZATIONS),
|
||||
scopes: toArrayConfig(process.env.CMD_GITHUB_SCOPES),
|
||||
},
|
||||
bitbucket: {
|
||||
clientID: process.env.CMD_BITBUCKET_CLIENTID,
|
||||
|
|
|
@ -80,6 +80,14 @@
|
|||
"description": "GitHub API client secret",
|
||||
"required": false
|
||||
},
|
||||
"CMD_GITHUB_ORGANIZATIONS": {
|
||||
"description": "GitHub whitelist of orgs",
|
||||
"required": false
|
||||
},
|
||||
"CMD_GITHUB_SCOPES": {
|
||||
"description": "GitHub OAuth API scopes",
|
||||
"required": false
|
||||
},
|
||||
"CMD_BITBUCKET_CLIENTID": {
|
||||
"description": "Bitbucket API client id",
|
||||
"required": false
|
||||
|
|
Loading…
Reference in New Issue