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.
- package/eslint.config.mjs +10 -0
- package/library.js +228 -242
- package/package.json +9 -2
- package/plugin.json +5 -1
- package/static/lib/admin.js +24 -25
package/library.js
CHANGED
|
@@ -1,242 +1,228 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
db.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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.
|
|
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.
|
|
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": {
|
package/static/lib/admin.js
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
});
|