nodebb-plugin-sso-github 3.1.2 → 3.2.0

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.
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ import serverConfig from 'eslint-config-nodebb';
4
+ import publicConfig from 'eslint-config-nodebb/public';
5
+
6
+ export default [
7
+ ...publicConfig,
8
+ ...serverConfig,
9
+ ];
10
+
package/library.js CHANGED
@@ -1,242 +1,228 @@
1
- (function(module) {
2
- "use strict";
3
-
4
- var User = require.main.require('./src/user');
5
- var db = require.main.require('./src/database');
6
- var meta = require.main.require('./src/meta');
7
- var nconf = require.main.require('nconf');
8
- var async = require.main.require('async');
9
- var passport = require.main.require('passport');
10
- var GithubStrategy = require('passport-github2').Strategy;
11
-
12
- var winston = require.main.require('winston');
13
-
14
- var authenticationController = require.main.require('./src/controllers/authentication');
15
-
16
- var constants = Object.freeze({
17
- 'name': "GitHub",
18
- 'admin': {
19
- 'icon': 'fa-github',
20
- 'route': '/plugins/sso-github'
21
- }
22
- });
23
-
24
- var GitHub = {};
25
-
26
- GitHub.getStrategy = function(strategies, callback) {
27
- meta.settings.get('sso-github', function(err, settings) {
28
- GitHub.settings = settings;
29
-
30
- if (!err && settings.id && settings.secret) {
31
- passport.use(new GithubStrategy({
32
- clientID: settings.id,
33
- clientSecret: settings.secret,
34
- callbackURL: nconf.get('url') + '/auth/github/callback',
35
- passReqToCallback: true,
36
- scope: [ 'user:email' ] // fetches non-public emails as well
37
- }, function(req, token, tokenSecret, profile, done) {
38
- if (req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') && req.user.uid > 0) {
39
- // Save GitHub -specific information to the user
40
- User.setUserField(req.user.uid, 'githubid', profile.id);
41
- db.setObjectField('githubid:uid', profile.id, req.user.uid);
42
- return done(null, req.user);
43
- }
44
-
45
- var email = Array.isArray(profile.emails) && profile.emails.length ? profile.emails[0].value : '';
46
- var pictureUrl = Array.isArray(profile.photos) && profile.photos.length ? profile.photos[0].value : '';
47
- GitHub.login(profile.id, profile.displayName, profile.username, email, pictureUrl, function(err, user) {
48
- if (err) {
49
- return done(err);
50
- }
51
-
52
- authenticationController.onSuccessfulLogin(req, user.uid);
53
- done(null, user);
54
- });
55
- }));
56
-
57
- strategies.push({
58
- name: 'github',
59
- url: '/auth/github',
60
- callbackURL: '/auth/github/callback',
61
- icon: constants.admin.icon,
62
- icons: {
63
- normal: 'fa-brands fa-github',
64
- square: 'fa-brands fa-github-square',
65
- },
66
- labels: {
67
- login: '[[social:sign-in-with-github]]',
68
- register: '[[social:sign-up-with-github]]',
69
- },
70
- color: '#25292f',
71
- scope: 'user:email'
72
- });
73
- }
74
-
75
- callback(null, strategies);
76
- });
77
- };
78
-
79
- GitHub.appendUserHashWhitelist = function (data, callback) {
80
- data.whitelist.push('githubid');
81
- setImmediate(callback, null, data);
82
- };
83
-
84
- GitHub.getAssociation = function(data, callback) {
85
- User.getUserField(data.uid, 'githubid', function(err, githubid) {
86
- if (err) {
87
- return callback(err, data);
88
- }
89
-
90
- if (githubid) {
91
- data.associations.push({
92
- associated: true,
93
- name: constants.name,
94
- icon: constants.admin.icon,
95
- deauthUrl: nconf.get('url') + '/deauth/github',
96
- });
97
- } else {
98
- data.associations.push({
99
- associated: false,
100
- url: nconf.get('url') + '/auth/github',
101
- name: constants.name,
102
- icon: constants.admin.icon
103
- });
104
- }
105
-
106
- callback(null, data);
107
- })
108
- };
109
-
110
- GitHub.login = function(githubID, displayName, username, email, pictureUrl, callback) {
111
- if (!email) {
112
- email = username + '@users.noreply.github.com';
113
- }
114
-
115
- GitHub.getUidByGitHubID(githubID, function(err, uid) {
116
- if (err) {
117
- return callback(err);
118
- }
119
-
120
- if (uid) {
121
- // Existing User
122
- callback(null, {
123
- uid: uid
124
- });
125
- } else {
126
- // New User
127
- var success = function(uid) {
128
- function checkEmail(next) {
129
- if (GitHub.settings.needToVerifyEmail === 'on') {
130
- return next();
131
- }
132
- User.email.confirmByUid(uid, next);
133
- }
134
-
135
- function mergeUserData(next) {
136
- async.waterfall([
137
- async.apply(User.getUserFields, uid, ['picture', 'firstName', 'lastName', 'fullname']),
138
- function(info, next) {
139
- if (!info.picture && pictureUrl) { // set profile picture
140
- User.setUserField(uid, 'uploadedpicture', pictureUrl);
141
- User.setUserField(uid, 'picture', pictureUrl);
142
- }
143
-
144
- if (!info.fullname && displayName) {
145
- User.setUserField(uid, 'fullname', displayName);
146
- }
147
- next();
148
- }
149
- ], next);
150
- }
151
-
152
- // trust the email.
153
- async.series([
154
- async.apply(User.setUserField, uid, 'githubid', githubID),
155
- async.apply(db.setObjectField, 'githubid:uid', githubID, uid),
156
- checkEmail,
157
- mergeUserData
158
- ], function (err) {
159
- callback(err, {
160
- uid: uid
161
- });
162
- });
163
- };
164
-
165
- User.getUidByEmail(email, function(err, uid) {
166
- if (!uid) {
167
- // Abort user creation if registration via SSO is restricted
168
- if (GitHub.settings.disableRegistration === 'on') {
169
- return callback(new Error('[[error:sso-registration-disabled, GitHub]]'));
170
- }
171
-
172
- User.create({username: username, email: email}, function(err, uid) {
173
- if (err !== null) {
174
- callback(err);
175
- } else {
176
- success(uid);
177
- }
178
- });
179
- } else {
180
- success(uid); // Existing account -- merge
181
- }
182
- });
183
- }
184
- });
185
- };
186
-
187
- GitHub.getUidByGitHubID = function(githubID, callback) {
188
- db.getObjectField('githubid:uid', githubID, function(err, uid) {
189
- if (err) {
190
- callback(err);
191
- } else {
192
- callback(null, uid);
193
- }
194
- });
195
- };
196
-
197
- GitHub.addMenuItem = function(custom_header, callback) {
198
- custom_header.authentication.push({
199
- "route": constants.admin.route,
200
- "icon": constants.admin.icon,
201
- "name": constants.name
202
- });
203
-
204
- callback(null, custom_header);
205
- };
206
-
207
- GitHub.init = function(data, callback) {
208
- const hostHelpers = require.main.require('./src/routes/helpers');
209
-
210
- hostHelpers.setupAdminPageRoute(data.router, '/admin/plugins/sso-github', function (req, res) {
211
- res.render('admin/plugins/sso-github', {
212
- title: constants.name,
213
- callbackURL: nconf.get('url') + '/auth/github/callback'
214
- });
215
- });
216
-
217
- hostHelpers.setupPageRoute(data.router, '/deauth/github', [data.middleware.requireUser], function (req, res) {
218
- res.render('plugins/sso-github/deauth', {
219
- service: "GitHub",
220
- });
221
- });
222
- data.router.post('/deauth/github', [data.middleware.requireUser, data.middleware.applyCSRF], hostHelpers.tryRoute(async function (req, res) {
223
- await GitHub.deleteUserData({
224
- uid: req.user.uid,
225
- });
226
- res.redirect(nconf.get('relative_path') + '/me/edit');
227
- }));
228
-
229
- callback();
230
- };
231
-
232
- GitHub.deleteUserData = async function(data) {
233
- const { uid } = data;
234
- const githubid = await User.getUserField(uid, 'githubid');
235
- if (githubid) {
236
- await db.deleteObjectField('githubid:uid', githubid, next);
237
- await db.deleteObjectField('user:' + uid, 'githubid');
238
- }
239
- };
240
-
241
- module.exports = GitHub;
242
- }(module));
1
+ 'use strict';
2
+
3
+ const User = require.main.require('./src/user');
4
+ const db = require.main.require('./src/database');
5
+ const meta = require.main.require('./src/meta');
6
+ const nconf = require.main.require('nconf');
7
+ const passport = require.main.require('passport');
8
+ const GithubStrategy = require('passport-github2').Strategy;
9
+
10
+ const constants = Object.freeze({
11
+ name: 'GitHub',
12
+ admin: {
13
+ icon: 'fa-github',
14
+ route: '/plugins/sso-github',
15
+ },
16
+ });
17
+
18
+ const GitHub = module.exports;
19
+
20
+ GitHub.init = async function (data) {
21
+ const hostHelpers = require.main.require('./src/routes/helpers');
22
+
23
+ hostHelpers.setupAdminPageRoute(data.router, '/admin/plugins/sso-github', function (req, res) {
24
+ res.render('admin/plugins/sso-github', {
25
+ title: constants.name,
26
+ callbackURL: nconf.get('url') + '/auth/github/callback',
27
+ });
28
+ });
29
+
30
+ hostHelpers.setupPageRoute(data.router, '/deauth/github', [data.middleware.requireUser], function (req, res) {
31
+ res.render('plugins/sso-github/deauth', {
32
+ service: constants.name,
33
+ });
34
+ });
35
+ data.router.post('/deauth/github', [data.middleware.requireUser, data.middleware.applyCSRF], hostHelpers.tryRoute(async function (req, res) {
36
+ await GitHub.deleteUserData({
37
+ uid: req.user.uid,
38
+ });
39
+ res.redirect(nconf.get('relative_path') + '/me/edit');
40
+ }));
41
+ };
42
+
43
+ GitHub.getStrategy = async function (strategies) {
44
+ const settings = await meta.settings.get('sso-github');
45
+ GitHub.settings = settings;
46
+
47
+ if (settings.id && settings.secret) {
48
+ passport.use(new GithubStrategy({
49
+ clientID: settings.id,
50
+ clientSecret: settings.secret,
51
+ callbackURL: nconf.get('url') + '/auth/github/callback',
52
+ passReqToCallback: true,
53
+ scope: ['user:email'], // fetches non-public emails as well
54
+ }, async function (req, token, tokenSecret, profile, done) {
55
+ try {
56
+ if (req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') && req.user.uid > 0) {
57
+ // Save GitHub -specific information to the user
58
+ await User.setUserField(req.user.uid, 'githubid', profile.id);
59
+ await db.setObjectField('githubid:uid', profile.id, req.user.uid);
60
+ return done(null, req.user);
61
+ }
62
+
63
+ const email = Array.isArray(profile.emails) && profile.emails.length ? profile.emails[0].value : '';
64
+ const pictureUrl = Array.isArray(profile.photos) && profile.photos.length ? profile.photos[0].value : '';
65
+ const { queued, uid, message } = await GitHub.login(
66
+ req, profile.id, profile.displayName, profile.username, email, pictureUrl
67
+ );
68
+ if (queued) {
69
+ return done(null, false, { message });
70
+ }
71
+
72
+ done(null, { uid });
73
+ } catch (err) {
74
+ done(err);
75
+ }
76
+ }));
77
+
78
+ strategies.push({
79
+ name: 'github',
80
+ url: '/auth/github',
81
+ callbackURL: '/auth/github/callback',
82
+ icon: constants.admin.icon,
83
+ icons: {
84
+ normal: 'fa-brands fa-github',
85
+ square: 'fa-brands fa-github-square',
86
+ },
87
+ labels: {
88
+ login: '[[social:sign-in-with-github]]',
89
+ register: '[[social:sign-up-with-github]]',
90
+ },
91
+ color: '#25292f',
92
+ scope: 'user:email',
93
+ });
94
+ }
95
+
96
+ return strategies;
97
+ };
98
+
99
+ GitHub.appendUserHashWhitelist = function (data) {
100
+ data.whitelist.push('githubid');
101
+ return data;
102
+ };
103
+
104
+ GitHub.getAssociation = async function (data) {
105
+ const githubid = await User.getUserField(data.uid, 'githubid');
106
+ if (githubid) {
107
+ data.associations.push({
108
+ associated: true,
109
+ name: constants.name,
110
+ icon: constants.admin.icon,
111
+ deauthUrl: nconf.get('url') + '/deauth/github',
112
+ });
113
+ } else {
114
+ data.associations.push({
115
+ associated: false,
116
+ url: nconf.get('url') + '/auth/github',
117
+ name: constants.name,
118
+ icon: constants.admin.icon,
119
+ });
120
+ }
121
+ return data;
122
+ };
123
+
124
+ GitHub.login = async function (req, githubID, displayName, username, email, pictureUrl) {
125
+ if (!email) {
126
+ email = username + '@users.noreply.github.com';
127
+ }
128
+
129
+ let uid = await GitHub.getUidByGitHubID(githubID);
130
+ if (uid) {
131
+ return { uid };
132
+ }
133
+
134
+ uid = await User.getUidByEmail(email);
135
+ if (uid) { // Link github account to existing user with same email
136
+ await Promise.all([
137
+ User.setUserField(uid, 'githubid', githubID),
138
+ db.setObjectField('githubid:uid', githubID, uid),
139
+ ]);
140
+ return { uid };
141
+ }
142
+
143
+ // Abort user creation if registration via SSO is restricted
144
+ if (GitHub.settings.disableRegistration === 'on') {
145
+ throw new Error('[[error:sso-registration-disabled, GitHub]]');
146
+ }
147
+
148
+ return await User.createOrQueue(req, {
149
+ githubid: githubID,
150
+ username: username,
151
+ email: email,
152
+ fullname: displayName,
153
+ picture: pictureUrl,
154
+ }, {
155
+ emailVerification: GitHub.settings.needToVerifyEmail === 'on' ? 'send' : 'verify',
156
+ });
157
+ };
158
+
159
+ GitHub.addToApprovalQueue = async (hookData) => {
160
+ await saveGitHubSpecificData(hookData.data, hookData.userData);
161
+ return hookData;
162
+ };
163
+
164
+ GitHub.filterUserCreate = async (hookData) => {
165
+ await saveGitHubSpecificData(hookData.user, hookData.data);
166
+ return hookData;
167
+ };
168
+
169
+ async function saveGitHubSpecificData(targetObj, sourceObj) {
170
+ const { githubid, picture } = sourceObj;
171
+ if (githubid) {
172
+ const uid = await GitHub.getUidByGitHubID(githubid);
173
+ if (uid) {
174
+ throw new Error('[[error:sso-account-exists, GitHub]]');
175
+ }
176
+ targetObj.githubid = githubid;
177
+ if (picture) {
178
+ targetObj.picture = picture;
179
+ targetObj.uploadedpicture = picture;
180
+ }
181
+ }
182
+ }
183
+
184
+ GitHub.actionUserCreate = async (hookData) => {
185
+ const { uid } = hookData.user;
186
+ const githubid = await User.getUserField(uid, 'githubid');
187
+ if (githubid) {
188
+ await db.setObjectField('githubid:uid', githubid, uid);
189
+ }
190
+ };
191
+
192
+ GitHub.filterUserGetRegistrationQueue = async (hookData) => {
193
+ const { users } = hookData;
194
+ users.forEach((user) => {
195
+ if (user?.githubid) {
196
+ user.sso = {
197
+ icon: 'fa-brands fa-github',
198
+ name: constants.name,
199
+ };
200
+ }
201
+ });
202
+ return hookData;
203
+ };
204
+
205
+ GitHub.getUidByGitHubID = async function (githubID) {
206
+ const uid = await db.getObjectField('githubid:uid', githubID);
207
+ return uid;
208
+ };
209
+
210
+ GitHub.addMenuItem = function (custom_header) {
211
+ custom_header.authentication.push({
212
+ route: constants.admin.route,
213
+ icon: constants.admin.icon,
214
+ name: constants.name,
215
+ });
216
+ return custom_header;
217
+ };
218
+
219
+ GitHub.deleteUserData = async function (data) {
220
+ const { uid } = data;
221
+ const githubid = await User.getUserField(uid, 'githubid');
222
+ if (githubid) {
223
+ await db.deleteObjectField('githubid:uid', githubid);
224
+ await db.deleteObjectField('user:' + uid, 'githubid');
225
+ }
226
+ };
227
+
228
+
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "nodebb-plugin-sso-github",
3
- "version": "3.1.2",
3
+ "version": "3.2.0",
4
4
  "description": "NodeBB GitHub SSO",
5
5
  "main": "library.js",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/julianlam/nodebb-plugin-sso-github"
9
9
  },
10
+ "scripts": {
11
+ "lint": "eslint ."
12
+ },
10
13
  "keywords": [
11
14
  "nodebb",
12
15
  "plugin",
@@ -45,7 +48,11 @@
45
48
  "dependencies": {
46
49
  "passport-github2": "^0.1.9"
47
50
  },
51
+ "devDependencies": {
52
+ "eslint": "10.0.2",
53
+ "eslint-config-nodebb": "^2.0.0"
54
+ },
48
55
  "nbbpm": {
49
- "compatibility": "^4.0.0"
56
+ "compatibility": "^4.9.0"
50
57
  }
51
58
  }
package/plugin.json CHANGED
@@ -10,7 +10,11 @@
10
10
  { "hook": "filter:auth.list", "method": "getAssociation" },
11
11
  { "hook": "filter:admin.header.build", "method": "addMenuItem" },
12
12
  { "hook": "static:user.delete", "method": "deleteUserData" },
13
- { "hook": "filter:user.whitelistFields", "method": "appendUserHashWhitelist" }
13
+ { "hook": "filter:user.whitelistFields", "method": "appendUserHashWhitelist" },
14
+ { "hook": "filter:user.addToApprovalQueue", "method": "addToApprovalQueue" },
15
+ { "hook": "filter:user.create", "method": "filterUserCreate" },
16
+ { "hook": "action:user.create", "method": "actionUserCreate" },
17
+ { "hook": "filter:user.getRegistrationQueue", "method": "filterUserGetRegistrationQueue" }
14
18
  ],
15
19
  "templates": "./templates",
16
20
  "modules": {
@@ -1,26 +1,25 @@
1
- define('admin/plugins/sso-github', ['settings', 'alerts'], function(Settings, alerts) {
2
- 'use strict';
3
- /* globals $, app, socket, require */
4
-
5
- var ACP = {};
6
-
7
- ACP.init = function() {
8
- Settings.load('sso-github', $('.sso-github-settings'));
9
-
10
- $('#save').on('click', function() {
11
- Settings.save('sso-github', $('.sso-github-settings'), function() {
12
- alerts.alert({
13
- type: 'success',
14
- alert_id: 'sso-github-saved',
15
- title: 'Settings Saved',
16
- message: 'Please reload your NodeBB to apply these settings',
17
- clickfn: function() {
18
- socket.emit('admin.reload');
19
- }
20
- });
21
- });
22
- });
23
- };
24
-
25
- return ACP;
1
+ 'use strict';
2
+
3
+ define('admin/plugins/sso-github', ['settings', 'alerts'], function (Settings, alerts) {
4
+ const ACP = {};
5
+
6
+ ACP.init = function () {
7
+ Settings.load('sso-github', $('.sso-github-settings'));
8
+
9
+ $('#save').on('click', function () {
10
+ Settings.save('sso-github', $('.sso-github-settings'), function () {
11
+ alerts.alert({
12
+ type: 'success',
13
+ alert_id: 'sso-github-saved',
14
+ title: 'Settings Saved',
15
+ message: 'Please reload your NodeBB to apply these settings',
16
+ clickfn: function () {
17
+ socket.emit('admin.reload');
18
+ },
19
+ });
20
+ });
21
+ });
22
+ };
23
+
24
+ return ACP;
26
25
  });