Compare commits

...

11 Commits

Author SHA1 Message Date
Evert Prants 1b15b4f3b7
new version 2019-09-06 19:03:42 +03:00
dependabot[bot] 37c3c04e86 Bump eslint-utils from 1.4.0 to 1.4.2
Bumps [eslint-utils](https://github.com/mysticatea/eslint-utils) from 1.4.0 to 1.4.2.
- [Release notes](https://github.com/mysticatea/eslint-utils/releases)
- [Commits](https://github.com/mysticatea/eslint-utils/compare/v1.4.0...v1.4.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-27 10:14:21 -04:00
Julian Lam 32e061cfe4 0.3.4 2019-07-15 11:21:07 -04:00
Julian Lam 68f3c84585 fix: bump dependencies 2019-07-15 11:21:06 -04:00
Julian Lam b82a4cf4f9 fix: some additional style changes 2019-01-18 12:09:45 -05:00
Julian Lam 2840b0568e fix: updated readme to add email address for commissions 2019-01-17 16:51:44 -05:00
Julian Lam 9f6ab199de fix: linted library.js 2019-01-17 16:51:32 -05:00
Julian Lam 8c57b5c105 feat: added eslint, husky, lint-staged to development workflow 2019-01-17 16:41:52 -05:00
Julian Lam 0ebdabd79a
Merge pull request #21 from LudwikJaniuk/fix-delete
Fix deleteUser
2018-08-07 14:44:31 -04:00
Ludvig Janiuk 0de77d75b2 comment mistake 2018-08-02 11:41:24 +02:00
Ludvig Janiuk 8cfdd45de7 Whitelist filter. Fix #20 2018-08-02 11:38:15 +02:00
8 changed files with 3320 additions and 67 deletions

137
.eslintrc Normal file
View File

@ -0,0 +1,137 @@
{
"extends": "airbnb-base",
"parserOptions": {
"sourceType": "script"
},
"rules": {
// Customized
"handle-callback-err": [ "error","^(e$|(e|(.*(_e|E)))rr)" ],
"comma-dangle": ["error", {
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "never"
}],
"no-empty": ["error", { "allowEmptyCatch": true }],
"no-underscore-dangle": "off",
"no-console": "off",
"no-mixed-operators": ["error", { "allowSamePrecedence": true }],
"strict": ["error", "global"],
"consistent-return": "off",
"func-names": "off",
"no-tabs": "off",
"indent": ["error", "tab"],
"no-eq-null": "off",
"camelcase": "off",
"no-new": "off",
"no-shadow": "off",
"no-use-before-define": ["error", "nofunc"],
"no-prototype-builtins": "off",
"new-cap": "off",
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
"object-curly-newline": "off",
"no-restricted-globals": "off",
"function-paren-newline": "off",
"import/no-unresolved": "error",
"quotes": ["error", "single", {
"avoidEscape": true,
"allowTemplateLiterals": true
}],
"no-else-return": [ "error", { "allowElseIf": true } ],
"operator-linebreak": [ "error", "after" ],
// ES6
"prefer-rest-params": "off",
"prefer-spread": "off",
"prefer-arrow-callback": "off",
"prefer-template": "off",
"no-var": "off",
"object-shorthand": "off",
"vars-on-top": "off",
"prefer-destructuring": "off",
// TODO
"import/no-extraneous-dependencies": "off",
"import/no-dynamic-require": "off",
"import/newline-after-import": "off",
"no-bitwise": "off",
"global-require": "off",
"max-len": "off",
"no-param-reassign": "off",
"no-restricted-syntax": "off",
"no-script-url": "off",
"default-case": "off",
"linebreak-style": "off",
// "no-multi-assign": "off",
// "one-var": "off",
// "no-undef": "off",
// "max-nested-callbacks": "off",
// "no-mixed-requires": "off",
// "brace-style": "off",
// "max-statements-per-line": "off",
// "no-unused-vars": "off",
// "no-mixed-spaces-and-tabs": "off",
// "no-useless-concat": "off",
// "require-jsdoc": "off",
// "eqeqeq": "off",
// "no-negated-condition": "off",
// "one-var-declaration-per-line": "off",
// "no-lonely-if": "off",
// "radix": "off",
// "no-else-return": "off",
// "no-useless-escape": "off",
// "block-scoped-var": "off",
// "operator-assignment": "off",
// "yoda": "off",
// "no-loop-func": "off",
// "no-void": "off",
// "valid-jsdoc": "off",
// "no-cond-assign": "off",
// "no-redeclare": "off",
// "no-unreachable": "off",
// "no-nested-ternary": "off",
// "operator-linebreak": "off",
// "guard-for-in": "off",
// "no-unneeded-ternary": "off",
// "no-sequences": "off",
// "no-extend-native": "off",
// "no-shadow-restricted-names": "off",
// "no-extra-boolean-cast": "off",
// "no-path-concat": "off",
// "no-unused-expressions": "off",
// "no-return-assign": "off",
// "no-restricted-modules": "off",
// "object-curly-spacing": "off",
// "indent": "off",
// "padded-blocks": "off",
// "eol-last": "off",
// "lines-around-directive": "off",
// "strict": "off",
// "comma-dangle": "off",
// "no-multi-spaces": "off",
// "quotes": "off",
// "keyword-spacing": "off",
// "no-mixed-operators": "off",
// "comma-spacing": "off",
// "no-trailing-spaces": "off",
// "key-spacing": "off",
// "no-multiple-empty-lines": "off",
// "spaced-comment": "off",
// "space-in-parens": "off",
// "block-spacing": "off",
// "quote-props": "off",
// "space-unary-ops": "off",
// "no-empty": "off",
// "dot-notation": "off",
// "func-call-spacing": "off",
// "array-bracket-spacing": "off",
// "object-property-newline": "off",
// "no-continue": "off",
// "no-extra-semi": "off",
// "no-spaced-func": "off",
// "no-useless-return": "off"
}
}

6
.gitignore vendored
View File

@ -216,3 +216,9 @@ pip-log.txt
sftp-config.json sftp-config.json
node_modules/ node_modules/
####################
# JetBrains
####################
.idea

View File

@ -14,4 +14,6 @@ NodeBB Plugin that allows users to login/register via any configured OAuth provi
## Trouble? ## Trouble?
The NodeBB team builds out SSO plugins for a nominal fee. [Reach out to us for a quote.](mailto:sales@nodebb.org)
Find us on [the community forums](http://community.nodebb.org)! Find us on [the community forums](http://community.nodebb.org)!

3
commitlint.config.js Normal file
View File

@ -0,0 +1,3 @@
'use strict';
module.exports = { extends: ['@commitlint/config-angular'] };

View File

@ -1,6 +1,6 @@
(function(module) { 'use strict';
"use strict";
(function (module) {
/* /*
Welcome to the SSO OAuth plugin! If you're inspecting this code, you're probably looking to Welcome to the SSO OAuth plugin! If you're inspecting this code, you're probably looking to
hook up NodeBB with your existing OAuth endpoint. hook up NodeBB with your existing OAuth endpoint.
@ -16,18 +16,16 @@
Step 4: If all goes well, you'll be able to login/register via your OAuth endpoint credentials. Step 4: If all goes well, you'll be able to login/register via your OAuth endpoint credentials.
*/ */
var User = module.parent.require('./user'), const User = require.main.require('./src/user');
Groups = module.parent.require('./groups'), const Groups = require.main.require('./src/groups');
meta = module.parent.require('./meta'), const db = require.main.require('./src/database');
db = module.parent.require('../src/database'), const authenticationController = require.main.require('./src/controllers/authentication');
passport = module.parent.require('passport'),
fs = module.parent.require('fs'),
path = module.parent.require('path'),
nconf = module.parent.require('nconf'),
winston = module.parent.require('winston'),
async = module.parent.require('async');
var authenticationController = module.parent.require('./controllers/authentication'); const async = require('async');
const passport = module.parent.require('passport');
const nconf = module.parent.require('nconf');
const winston = module.parent.require('winston');
/** /**
* REMEMBER * REMEMBER
@ -48,27 +46,30 @@
* `OAUTH__ID=someoauthid OAUTH__SECRET=youroauthsecret node app.js` * `OAUTH__ID=someoauthid OAUTH__SECRET=youroauthsecret node app.js`
*/ */
var constants = Object.freeze({ const constants = Object.freeze({
type: 'oauth2', // Either 'oauth' or 'oauth2' type: 'oauth2', // Either 'oauth' or 'oauth2'
name: 'icynet', // Something unique to your OAuth provider in lowercase, like "github", or "nodebb" name: 'icynet', // Something unique to your OAuth provider in lowercase, like "github", or "nodebb"
scope: 'email privilege', scope: 'email privilege',
oauth: { oauth: {
requestTokenURL: '', requestTokenURL: '',
accessTokenURL: '', accessTokenURL: '',
userAuthorizationURL: '', userAuthorizationURL: '',
consumerKey: nconf.get('oauth:key'), // don't change this line consumerKey: nconf.get('oauth:key'), // don't change this line
consumerSecret: nconf.get('oauth:secret'), // don't change this line consumerSecret: nconf.get('oauth:secret'), // don't change this line
}, },
oauth2: { oauth2: {
authorizationURL: nconf.get('oauth:provider') + '/oauth2/authorize', authorizationURL: nconf.get('oauth:provider') + '/oauth2/authorize',
tokenURL: nconf.get('oauth:provider') + '/oauth2/token', tokenURL: nconf.get('oauth:provider') + '/oauth2/token',
clientID: nconf.get('oauth:id'), clientID: nconf.get('oauth:id'),
clientSecret: nconf.get('oauth:secret'), clientSecret: nconf.get('oauth:secret'),
}, },
userRoute: nconf.get('oauth:provider') + '/oauth2/user' userRoute: nconf.get('oauth:provider') + '/oauth2/user'
}), })
configOk = false,
OAuth = {}, passportOAuth, opts; const OAuth = {};
let configOk = false;
let passportOAuth;
let opts;
if (!constants.name) { if (!constants.name) {
winston.error('[sso-oauth] Please specify a name for your OAuth provider (library.js:32)'); winston.error('[sso-oauth] Please specify a name for your OAuth provider (library.js:32)');
@ -80,7 +81,7 @@
configOk = true; configOk = true;
} }
OAuth.getStrategy = function(strategies, callback) { OAuth.getStrategy = function (strategies, callback) {
if (configOk) { if (configOk) {
passportOAuth = require('passport-oauth')[constants.type === 'oauth' ? 'OAuthStrategy' : 'OAuth2Strategy']; passportOAuth = require('passport-oauth')[constants.type === 'oauth' ? 'OAuthStrategy' : 'OAuth2Strategy'];
@ -89,19 +90,21 @@
opts = constants.oauth; opts = constants.oauth;
opts.callbackURL = nconf.get('url') + '/auth/' + constants.name + '/callback'; opts.callbackURL = nconf.get('url') + '/auth/' + constants.name + '/callback';
passportOAuth.Strategy.prototype.userProfile = function(token, secret, params, done) { passportOAuth.Strategy.prototype.userProfile = function (token, secret, params, done) {
this._oauth.get(constants.userRoute, token, secret, function(err, body, res) { this._oauth.get(constants.userRoute, token, secret, function (err, body/* , res */) {
if (err) { return done(new InternalOAuthError('failed to fetch user profile', err)); } if (err) {
return done(err);
}
try { try {
var json = JSON.parse(body); var json = JSON.parse(body);
OAuth.parseUserReturn(json, function(err, profile) { OAuth.parseUserReturn(json, function (err, profile) {
if (err) return done(err); if (err) return done(err);
profile.provider = constants.name; profile.provider = constants.name;
done(null, profile); done(null, profile);
}); });
} catch(e) { } catch (e) {
done(e); done(e);
} }
}); });
@ -111,19 +114,21 @@
opts = constants.oauth2; opts = constants.oauth2;
opts.callbackURL = nconf.get('url') + '/auth/' + constants.name + '/callback'; opts.callbackURL = nconf.get('url') + '/auth/' + constants.name + '/callback';
passportOAuth.Strategy.prototype.userProfile = function(accessToken, done) { passportOAuth.Strategy.prototype.userProfile = function (accessToken, done) {
this._oauth2.get(constants.userRoute, accessToken, function(err, body, res) { this._oauth2.get(constants.userRoute, accessToken, function (err, body/* , res */) {
if (err) { return done(new InternalOAuthError('failed to fetch user profile', err)); } if (err) {
return done(err);
}
try { try {
var json = JSON.parse(body); var json = JSON.parse(body);
OAuth.parseUserReturn(json, function(err, profile) { OAuth.parseUserReturn(json, function (err, profile) {
if (err) return done(err); if (err) return done(err);
profile.provider = constants.name; profile.provider = constants.name;
done(null, profile); done(null, profile);
}); });
} catch(e) { } catch (e) {
done(e); done(e);
} }
}); });
@ -132,7 +137,7 @@
opts.passReqToCallback = true; opts.passReqToCallback = true;
passport.use(constants.name, new passportOAuth(opts, function(req, token, secret, profile, done) { passport.use(constants.name, new passportOAuth(opts, function (req, token, secret, profile, done) {
OAuth.login({ OAuth.login({
oAuthid: profile.id, oAuthid: profile.id,
handle: profile.displayName, handle: profile.displayName,
@ -154,7 +159,7 @@
url: '/auth/' + constants.name, url: '/auth/' + constants.name,
callbackURL: '/auth/' + constants.name + '/callback', callbackURL: '/auth/' + constants.name + '/callback',
icon: 'fa-check-square', icon: 'fa-check-square',
scope: (constants.scope || '').split(',') scope: (constants.scope || '').split(','),
}); });
callback(null, strategies); callback(null, strategies);
@ -163,7 +168,7 @@
} }
}; };
OAuth.parseUserReturn = function(data, callback) { OAuth.parseUserReturn = function (data, callback) {
// Alter this section to include whatever data is necessary // Alter this section to include whatever data is necessary
// NodeBB *requires* the following: id, displayName, emails. // NodeBB *requires* the following: id, displayName, emails.
// Everything else is optional. // Everything else is optional.
@ -185,42 +190,43 @@
//process.stdout.write('===\nAt this point, you\'ll need to customise the above section to id, displayName, and emails into the "profile" object.\n==='); //process.stdout.write('===\nAt this point, you\'ll need to customise the above section to id, displayName, and emails into the "profile" object.\n===');
//return callback(new Error('Congrats! So far so good -- please see server log for details')); //return callback(new Error('Congrats! So far so good -- please see server log for details'));
// eslint-disable-next-line
callback(null, profile); callback(null, profile);
} };
OAuth.login = function(payload, callback) { OAuth.login = function (payload, callback) {
OAuth.getUidByOAuthid(payload.oAuthid, function(err, uid) { OAuth.getUidByOAuthid(payload.oAuthid, function (err, uid) {
if(err) { if (err) {
return callback(err); return callback(err);
} }
if (uid !== null) { if (uid !== null) {
// Existing User // Existing User
callback(null, { callback(null, {
uid: uid uid: uid,
}); });
} else { } else {
// New User // New User
var success = function(uid) { var success = function (uid) {
// Save provider-specific information to the user // Save provider-specific information to the user
User.setUserField(uid, constants.name + 'Id', payload.oAuthid); User.setUserField(uid, constants.name + 'Id', payload.oAuthid);
db.setObjectField(constants.name + 'Id:uid', payload.oAuthid, uid); db.setObjectField(constants.name + 'Id:uid', payload.oAuthid, uid);
if (payload.isAdmin) { if (payload.isAdmin) {
Groups.join('administrators', uid, function(err) { Groups.join('administrators', uid, function (err) {
callback(null, { callback(err, {
uid: uid uid: uid,
}); });
}); });
} else { } else {
callback(null, { callback(null, {
uid: uid uid: uid,
}); });
} }
}; };
User.getUidByEmail(payload.email, function(err, uid) { User.getUidByEmail(payload.email, function (err, uid) {
if(err) { if (err) {
return callback(err); return callback(err);
} }
@ -244,8 +250,8 @@
}); });
}; };
OAuth.getUidByOAuthid = function(oAuthid, callback) { OAuth.getUidByOAuthid = function (oAuthid, callback) {
db.getObjectField(constants.name + 'Id:uid', oAuthid, function(err, uid) { db.getObjectField(constants.name + 'Id:uid', oAuthid, function (err, uid) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
@ -253,13 +259,13 @@
}); });
}; };
OAuth.deleteUserData = function(data, callback) { OAuth.deleteUserData = function (data, callback) {
async.waterfall([ async.waterfall([
async.apply(User.getUserField, data.uid, constants.name + 'Id'), async.apply(User.getUserField, data.uid, constants.name + 'Id'),
function(oAuthIdToDelete, next) { function (oAuthIdToDelete, next) {
db.deleteObjectField(constants.name + 'Id:uid', oAuthIdToDelete, next); db.deleteObjectField(constants.name + 'Id:uid', oAuthIdToDelete, next);
} },
], function(err) { ], function (err) {
if (err) { if (err) {
winston.error('[sso-oauth] Could not remove OAuthId data for uid ' + data.uid + '. Error: ' + err); winston.error('[sso-oauth] Could not remove OAuthId data for uid ' + data.uid + '. Error: ' + err);
return callback(err); return callback(err);
@ -269,5 +275,11 @@
}); });
}; };
// If this filter is not there, the deleteUserData function will fail when getting the oauthId for deletion.
OAuth.whitelistFields = function (params, callback) {
params.whitelist.push(constants.name + 'Id');
callback(null, params);
};
module.exports = OAuth; module.exports = OAuth;
}(module)); }(module));

View File

@ -1,6 +1,6 @@
{ {
"name": "nodebb-plugin-sso-oauth", "name": "nodebb-plugin-sso-oauth",
"version": "0.3.3", "version": "0.3.4",
"description": "NodeBB Generic OAuth SSO", "description": "NodeBB Generic OAuth SSO",
"main": "library.js", "main": "library.js",
"repository": { "repository": {
@ -28,10 +28,32 @@
"readme": "", "readme": "",
"readmeFilename": "README.md", "readmeFilename": "README.md",
"dependencies": { "dependencies": {
"async": "^2",
"passport-oauth": "~1.0.0" "passport-oauth": "~1.0.0"
}, },
"nbbpm": { "nbbpm": {
"compatibility": "^1.0.1", "compatibility": "^1.0.1",
"index": false "index": false
},
"devDependencies": {
"@commitlint/cli": "^8.0.0",
"@commitlint/config-angular": "^7.1.2",
"eslint": "^5.16.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.8.0",
"husky": "^2.4.0",
"lint-staged": "^8.2.0"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"lint-staged": {
"*.js": [
"eslint --fix",
"git add"
]
} }
} }

View File

@ -6,6 +6,7 @@
"library": "./library.js", "library": "./library.js",
"hooks": [ "hooks": [
{ "hook": "static:user.delete", "method": "deleteUserData" }, { "hook": "static:user.delete", "method": "deleteUserData" },
{ "hook": "filter:user.whitelistFields", "method": "whitelistFields" },
{ "hook": "filter:auth.init", "method": "getStrategy" } { "hook": "filter:auth.init", "method": "getStrategy" }
] ]
} }

3070
yarn.lock Normal file

File diff suppressed because it is too large Load Diff