ep_openid_connect 2.0.5 → 2.0.9
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/.github/workflows/backend-tests.yml +20 -9
- package/.github/workflows/frontend-tests.yml +92 -0
- package/.github/workflows/npmpublish.yml +12 -8
- package/README.md +12 -2
- package/index.js +77 -26
- package/package.json +10 -9
- package/static/js/client.js +2 -3
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
# You need to change lines 38 and 46 in case the plugin's name on npmjs.com is different
|
|
2
|
-
# from the repository name
|
|
3
|
-
|
|
4
1
|
name: "Backend tests"
|
|
5
2
|
|
|
6
3
|
# any branch is useful for testing before a PR is submitted
|
|
@@ -17,6 +14,10 @@ jobs:
|
|
|
17
14
|
runs-on: ubuntu-latest
|
|
18
15
|
|
|
19
16
|
steps:
|
|
17
|
+
- uses: actions/setup-node@v2
|
|
18
|
+
with:
|
|
19
|
+
node-version: 12
|
|
20
|
+
|
|
20
21
|
- name: Install libreoffice
|
|
21
22
|
run: |
|
|
22
23
|
sudo add-apt-repository -y ppa:libreoffice/ppa
|
|
@@ -32,19 +33,29 @@ jobs:
|
|
|
32
33
|
- name: Install all dependencies and symlink for ep_etherpad-lite
|
|
33
34
|
run: src/bin/installDeps.sh
|
|
34
35
|
|
|
35
|
-
# clone this repository into node_modules/ep_plugin-name
|
|
36
36
|
- name: Checkout plugin repository
|
|
37
37
|
uses: actions/checkout@v2
|
|
38
38
|
with:
|
|
39
|
-
path: ./node_modules
|
|
39
|
+
path: ./node_modules/__tmp
|
|
40
|
+
|
|
41
|
+
- name: Determine plugin name
|
|
42
|
+
id: plugin_name
|
|
43
|
+
run: |
|
|
44
|
+
cd ./node_modules/__tmp
|
|
45
|
+
npx -c 'printf %s\\n "::set-output name=plugin_name::${npm_package_name}"'
|
|
46
|
+
|
|
47
|
+
- name: Rename plugin directory
|
|
48
|
+
run: |
|
|
49
|
+
mv ./node_modules/__tmp ./node_modules/"${PLUGIN_NAME}"
|
|
50
|
+
env:
|
|
51
|
+
PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }}
|
|
40
52
|
|
|
41
53
|
- name: Install plugin dependencies
|
|
42
54
|
run: |
|
|
43
|
-
cd node_modules
|
|
55
|
+
cd ./node_modules/"${PLUGIN_NAME}"
|
|
44
56
|
npm ci
|
|
57
|
+
env:
|
|
58
|
+
PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }}
|
|
45
59
|
|
|
46
60
|
- name: Run the backend tests
|
|
47
61
|
run: cd src && npm test
|
|
48
|
-
|
|
49
|
-
##ETHERPAD_NPM_V=2
|
|
50
|
-
## NPM configuration automatically created using src/bin/plugins/updateAllPluginsScript.sh
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Publicly credit Sauce Labs because they generously support open source
|
|
2
|
+
# projects.
|
|
3
|
+
name: "frontend tests powered by Sauce Labs"
|
|
4
|
+
|
|
5
|
+
on: [push]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
|
|
11
|
+
steps:
|
|
12
|
+
- name: Generate Sauce Labs strings
|
|
13
|
+
id: sauce_strings
|
|
14
|
+
run: |
|
|
15
|
+
printf %s\\n '::set-output name=name::${{github.event.repository.name}} ${{ github.workflow }} - ${{ github.job }}'
|
|
16
|
+
printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}'
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-node@v2
|
|
19
|
+
with:
|
|
20
|
+
node-version: 12
|
|
21
|
+
|
|
22
|
+
- name: Check out Etherpad core
|
|
23
|
+
uses: actions/checkout@v2
|
|
24
|
+
with:
|
|
25
|
+
repository: ether/etherpad-lite
|
|
26
|
+
|
|
27
|
+
- name: Check out the plugin
|
|
28
|
+
uses: actions/checkout@v2
|
|
29
|
+
with:
|
|
30
|
+
path: ./node_modules/__tmp
|
|
31
|
+
|
|
32
|
+
- name: export GIT_HASH to env
|
|
33
|
+
id: environment
|
|
34
|
+
run: |
|
|
35
|
+
cd ./node_modules/__tmp
|
|
36
|
+
echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})"
|
|
37
|
+
|
|
38
|
+
- name: Determine plugin name
|
|
39
|
+
id: plugin_name
|
|
40
|
+
run: |
|
|
41
|
+
cd ./node_modules/__tmp
|
|
42
|
+
npx -c 'printf %s\\n "::set-output name=plugin_name::${npm_package_name}"'
|
|
43
|
+
|
|
44
|
+
- name: Rename plugin directory
|
|
45
|
+
env:
|
|
46
|
+
PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }}
|
|
47
|
+
run: |
|
|
48
|
+
mv ./node_modules/__tmp ./node_modules/"${PLUGIN_NAME}"
|
|
49
|
+
|
|
50
|
+
- name: Install plugin dependencies
|
|
51
|
+
env:
|
|
52
|
+
PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }}
|
|
53
|
+
run: |
|
|
54
|
+
cd ./node_modules/"${PLUGIN_NAME}"
|
|
55
|
+
npm ci
|
|
56
|
+
|
|
57
|
+
# This must be run after setting up the plugin, otherwise npm will try to
|
|
58
|
+
# hoist common dependencies by removing them from src/node_modules and
|
|
59
|
+
# installing them in the top-level node_modules. As of v6.14.10, npm's hoist
|
|
60
|
+
# logic appears to be buggy, because it sometimes removes dependencies from
|
|
61
|
+
# src/node_modules but fails to add them to the top-level node_modules. Even
|
|
62
|
+
# if npm correctly hoists the dependencies, the hoisting seems to confuse
|
|
63
|
+
# tools such as `npm outdated`, `npm update`, and some ESLint rules.
|
|
64
|
+
- name: Install Etherpad core dependencies
|
|
65
|
+
run: src/bin/installDeps.sh
|
|
66
|
+
|
|
67
|
+
- name: Create settings.json
|
|
68
|
+
run: cp settings.json.template settings.json
|
|
69
|
+
|
|
70
|
+
- name: Disable import/export rate limiting
|
|
71
|
+
run: |
|
|
72
|
+
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 0/' -i settings.json
|
|
73
|
+
|
|
74
|
+
- name: Remove standard frontend test files
|
|
75
|
+
run: rm -rf src/tests/frontend/specs
|
|
76
|
+
|
|
77
|
+
- uses: saucelabs/sauce-connect-action@v1
|
|
78
|
+
with:
|
|
79
|
+
username: ${{ secrets.SAUCE_USERNAME }}
|
|
80
|
+
accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
|
|
81
|
+
tunnelIdentifier: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
|
82
|
+
|
|
83
|
+
- name: Run the frontend tests
|
|
84
|
+
shell: bash
|
|
85
|
+
env:
|
|
86
|
+
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
|
|
87
|
+
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
|
|
88
|
+
SAUCE_NAME: ${{ steps.sauce_strings.outputs.name }}
|
|
89
|
+
TRAVIS_JOB_NUMBER: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
|
90
|
+
GIT_HASH: ${{ steps.environment.outputs.sha_short }}
|
|
91
|
+
run: |
|
|
92
|
+
src/tests/frontend/travis/runner.sh
|
|
@@ -56,15 +56,22 @@ jobs:
|
|
|
56
56
|
runs-on: ubuntu-latest
|
|
57
57
|
steps:
|
|
58
58
|
- uses: actions/checkout@v2
|
|
59
|
+
with:
|
|
60
|
+
fetch-depth: 0
|
|
59
61
|
- uses: actions/setup-node@v1
|
|
60
62
|
with:
|
|
61
63
|
node-version: 12
|
|
62
64
|
registry-url: https://registry.npmjs.org/
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
- name: Bump version (patch)
|
|
66
|
+
run: |
|
|
67
|
+
LATEST_TAG=$(git describe --tags --abbrev=0) || exit 1
|
|
68
|
+
NEW_COMMITS=$(git rev-list --count "${LATEST_TAG}"..) || exit 1
|
|
69
|
+
[ "${NEW_COMMITS}" -gt 0 ] || exit 0
|
|
70
|
+
git config user.name 'github-actions[bot]'
|
|
71
|
+
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
|
72
|
+
npm ci
|
|
73
|
+
npm version patch
|
|
74
|
+
git push --follow-tags
|
|
68
75
|
# `npm publish` must come after `git push` otherwise there is a race
|
|
69
76
|
# condition: If two PRs are merged back-to-back then master/main will be
|
|
70
77
|
# updated with the commits from the second PR before the first PR's
|
|
@@ -78,6 +85,3 @@ jobs:
|
|
|
78
85
|
- run: npm publish
|
|
79
86
|
env:
|
|
80
87
|
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
81
|
-
|
|
82
|
-
##ETHERPAD_NPM_V=2
|
|
83
|
-
## NPM configuration automatically created using bin/plugins/updateAllPluginsScript.sh
|
package/README.md
CHANGED
|
@@ -34,8 +34,18 @@ Etherpad's `requireAuthentication` setting must be `true`.
|
|
|
34
34
|
|
|
35
35
|
### Configuration Details
|
|
36
36
|
|
|
37
|
-
* `issuer` (required): The base URL of the
|
|
38
|
-
used to
|
|
37
|
+
* `issuer` (required if `issuer_metadata` is not set): The base URL of the
|
|
38
|
+
OpenID Connect identity provider, used to discover the relevant OpenID Connect
|
|
39
|
+
endpoints. If set, your identity provider must support the [OpenID Connect
|
|
40
|
+
Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html)
|
|
41
|
+
protocol.
|
|
42
|
+
* `issuer_metadata` (required if `issuer` is not set): Object containing details
|
|
43
|
+
about your OpenID Connect identity provider. Used for manual configuration if
|
|
44
|
+
your identity provider does not support the Discovery protocol. If your
|
|
45
|
+
identity provider does support Discovery, you are encouraged to set `issuer`
|
|
46
|
+
instead. For properties, see the [documentation for the openid-client `Issuer`
|
|
47
|
+
object
|
|
48
|
+
constructor](https://github.com/panva/node-openid-client/blob/v4.7.4/docs/README.md#new-issuermetadata).
|
|
39
49
|
* `client_id` (required): The OAuth2 client ID issued by the identity provider.
|
|
40
50
|
* `client_secret` (required): The OAuth2 client secret issued by the identity
|
|
41
51
|
provider.
|
package/index.js
CHANGED
|
@@ -7,41 +7,88 @@ const authorManager = require('ep_etherpad-lite/node/db/AuthorManager');
|
|
|
7
7
|
|
|
8
8
|
const pluginName = 'ep_openid_connect';
|
|
9
9
|
const logger = log4js.getLogger(pluginName);
|
|
10
|
-
const settings = {
|
|
10
|
+
const settings = {
|
|
11
|
+
displayname_claim: 'name',
|
|
12
|
+
response_types: ['code'],
|
|
13
|
+
permit_displayname_change: false,
|
|
14
|
+
prohibited_usernames: ['admin', 'guest'],
|
|
15
|
+
};
|
|
11
16
|
let oidcClient = null;
|
|
12
17
|
|
|
13
18
|
const ep = (endpoint) => `/${pluginName}/${endpoint}`;
|
|
14
19
|
const endpointUrl = (endpoint) => new URL(ep(endpoint).substr(1), settings.base_url).toString();
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
const discoverIssuer = async (issuerUrl) => {
|
|
22
|
+
issuerUrl = new URL(issuerUrl);
|
|
23
|
+
// https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery says that the URI
|
|
24
|
+
// must not have query or fragment components.
|
|
25
|
+
if (issuerUrl.search) {
|
|
26
|
+
throw new Error(`Unexpected query in issuer URL (${issuerUrl}): ${issuerUrl.search}`);
|
|
27
|
+
}
|
|
28
|
+
if (issuerUrl.hash) {
|
|
29
|
+
throw new Error(`Unexpected fragment in issuer URL (${issuerUrl}): ${issuerUrl.hash}`);
|
|
30
|
+
}
|
|
31
|
+
let issuer;
|
|
32
|
+
try {
|
|
33
|
+
issuer = await Issuer.discover(issuerUrl.href);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
// The URL used to get the issuer metadata doesn't exactly follow RFC 8615; see:
|
|
36
|
+
// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
|
|
37
|
+
const discoveryUrl = new URL(issuerUrl);
|
|
38
|
+
if (!discoveryUrl.pathname.includes('/.well-known/')) {
|
|
39
|
+
discoveryUrl.pathname =
|
|
40
|
+
`${discoveryUrl.pathname.replace(/\/$/, '')}/.well-known/openid-configuration`;
|
|
41
|
+
}
|
|
42
|
+
logger.error(
|
|
43
|
+
'Failed to discover issuer metadata via OpenID Connect Discovery ' +
|
|
44
|
+
'(https://openid.net/specs/openid-connect-discovery-1_0.html). ' +
|
|
45
|
+
`Does your issuer support Discovery? (hint: ${discoveryUrl})`);
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
logger.info('OpenID Connect Discovery complete.');
|
|
49
|
+
return issuer;
|
|
50
|
+
};
|
|
18
51
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
52
|
+
const getIssuer = async (settings) => {
|
|
53
|
+
if (settings.issuer) return await discoverIssuer(settings.issuer);
|
|
54
|
+
return new Issuer(settings.issuer_metadata);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
exports.loadSettings = async (hookName, {settings: {[pluginName]: config = {}}}) => {
|
|
58
|
+
Object.assign(settings, config);
|
|
59
|
+
for (const setting of ['base_url', 'client_id', 'client_secret']) {
|
|
60
|
+
if (!settings[setting]) {
|
|
61
|
+
logger.error(`Required setting missing from settings.json: ${pluginName}.${setting}`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!settings.issuer && !settings.issuer_metadata) {
|
|
66
|
+
logger.error(`Either ${pluginName}.issuer or ${pluginName}.issuer_metadata must be set`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (settings.issuer && settings.issuer_metadata) {
|
|
70
|
+
logger.warn(
|
|
71
|
+
`Ignoring ${pluginName}.issuer_metadata setting because ${pluginName}.issuer is set`);
|
|
22
72
|
}
|
|
23
|
-
Object.assign(settings, mySettings);
|
|
24
73
|
// Make sure base_url ends with '/' so that relative URLs are appended:
|
|
25
74
|
if (!settings.base_url.endsWith('/')) settings.base_url += '/';
|
|
26
|
-
|
|
27
|
-
settings.response_types = settings.response_types || ['code'];
|
|
28
|
-
settings.permit_displayname_change = settings.permit_displayname_change || false;
|
|
29
|
-
settings.prohibited_usernames = settings.prohibited_usernames || ['admin', 'guest'];
|
|
30
|
-
oidcClient = new (await Issuer.discover(settings.issuer)).Client({
|
|
75
|
+
oidcClient = new (await getIssuer(settings)).Client({
|
|
31
76
|
client_id: settings.client_id,
|
|
32
77
|
client_secret: settings.client_secret,
|
|
33
78
|
response_types: settings.response_types,
|
|
34
79
|
redirect_uris: [endpointUrl('callback')],
|
|
35
80
|
});
|
|
36
|
-
logger.info('
|
|
81
|
+
logger.info('Configured.');
|
|
37
82
|
};
|
|
38
83
|
|
|
39
|
-
exports.clientVars = (hookName, context
|
|
84
|
+
exports.clientVars = (hookName, context) => {
|
|
85
|
+
if (oidcClient == null) return;
|
|
40
86
|
const {permit_displayname_change} = settings;
|
|
41
|
-
return
|
|
87
|
+
return {[pluginName]: {permit_displayname_change}};
|
|
42
88
|
};
|
|
43
89
|
|
|
44
90
|
exports.expressCreateServer = (hookName, {app}) => {
|
|
91
|
+
if (oidcClient == null) return;
|
|
45
92
|
logger.debug('Configuring auth routes');
|
|
46
93
|
app.get(ep('callback'), (req, res, next) => {
|
|
47
94
|
logger.debug(`Processing ${req.url}`);
|
|
@@ -79,7 +126,8 @@ exports.expressCreateServer = (hookName, {app}) => {
|
|
|
79
126
|
app.get(ep('logout'), (req, res) => req.session.destroy(() => res.redirect(settings.base_url)));
|
|
80
127
|
};
|
|
81
128
|
|
|
82
|
-
exports.authenticate = (hookName, {req, res, users}
|
|
129
|
+
exports.authenticate = (hookName, {req, res, users}) => {
|
|
130
|
+
if (oidcClient == null) return;
|
|
83
131
|
logger.debug('authenticate hook for', req.url);
|
|
84
132
|
const {session} = req;
|
|
85
133
|
const {[pluginName]: {userinfo = {}} = {}} = session;
|
|
@@ -89,29 +137,31 @@ exports.authenticate = (hookName, {req, res, users}, cb) => {
|
|
|
89
137
|
// force regeneration.
|
|
90
138
|
delete session[pluginName];
|
|
91
139
|
// Authn failed. Let another plugin try to authenticate the user.
|
|
92
|
-
return
|
|
140
|
+
return;
|
|
93
141
|
}
|
|
94
142
|
// Successfully authenticated.
|
|
95
143
|
if (users[sub] == null) users[sub] = {};
|
|
96
144
|
session.user = users[sub];
|
|
97
145
|
session.user.username = sub;
|
|
98
146
|
session.user.displayname = userinfo[settings.displayname_claim] || session.user.displayname;
|
|
99
|
-
return
|
|
147
|
+
return true;
|
|
100
148
|
};
|
|
101
149
|
|
|
102
|
-
exports.authnFailure = (hookName, {req, res}
|
|
150
|
+
exports.authnFailure = (hookName, {req, res}) => {
|
|
151
|
+
if (oidcClient == null) return;
|
|
103
152
|
const url = new URL(req.url.substr(1), settings.base_url).toString();
|
|
104
153
|
res.redirect(`${endpointUrl('login')}?redirect_uri=${encodeURIComponent(url)}`);
|
|
105
|
-
return
|
|
154
|
+
return true;
|
|
106
155
|
};
|
|
107
156
|
|
|
108
|
-
exports.handleMessage = async (hookName, {message,
|
|
157
|
+
exports.handleMessage = async (hookName, {message, socket}) => {
|
|
158
|
+
if (oidcClient == null) return;
|
|
109
159
|
logger.debug('handleMessage hook', message);
|
|
110
|
-
const {user: {displayname} = {}} =
|
|
160
|
+
const {user: {displayname} = {}} = socket.client.request.session;
|
|
111
161
|
if (!displayname) return;
|
|
112
162
|
if (message.type === 'CLIENT_READY') {
|
|
113
163
|
logger.debug(
|
|
114
|
-
`CLIENT_READY ${
|
|
164
|
+
`CLIENT_READY ${socket.id}: Setting username for token ${message.token} to ${displayname}`
|
|
115
165
|
);
|
|
116
166
|
// TODO: author ID might come from session ID, not token.
|
|
117
167
|
const authorId = await authorManager.getAuthor4Token(message.token);
|
|
@@ -123,7 +173,8 @@ exports.handleMessage = async (hookName, {message, client}) => {
|
|
|
123
173
|
}
|
|
124
174
|
};
|
|
125
175
|
|
|
126
|
-
exports.preAuthorize = (hookName, {req}
|
|
127
|
-
if (
|
|
128
|
-
|
|
176
|
+
exports.preAuthorize = (hookName, {req}) => {
|
|
177
|
+
if (oidcClient == null) return;
|
|
178
|
+
if (req.path.startsWith(ep(''))) return true;
|
|
179
|
+
return;
|
|
129
180
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ep_openid_connect",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"description": "Etherpad plugin to authenticate users against an OpenID Connect provider.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -23,17 +23,18 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://github.com/ether/ep_openid_connect#readme",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"openid-client": "^
|
|
26
|
+
"openid-client": "^4.7.4"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"eslint": "^7.
|
|
30
|
-
"eslint-config-etherpad": "^
|
|
29
|
+
"eslint": "^7.32.0",
|
|
30
|
+
"eslint-config-etherpad": "^2.0.2",
|
|
31
|
+
"eslint-plugin-cypress": "^2.12.1",
|
|
31
32
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
32
|
-
"eslint-plugin-mocha": "^
|
|
33
|
+
"eslint-plugin-mocha": "^9.0.0",
|
|
33
34
|
"eslint-plugin-node": "^11.1.0",
|
|
34
35
|
"eslint-plugin-prefer-arrow": "^1.2.3",
|
|
35
|
-
"eslint-plugin-promise": "^
|
|
36
|
-
"eslint-plugin-you-dont-need-lodash-underscore": "^6.
|
|
36
|
+
"eslint-plugin-promise": "^5.1.1",
|
|
37
|
+
"eslint-plugin-you-dont-need-lodash-underscore": "^6.12.0"
|
|
37
38
|
},
|
|
38
39
|
"eslintConfig": {
|
|
39
40
|
"root": true,
|
|
@@ -45,12 +46,12 @@
|
|
|
45
46
|
},
|
|
46
47
|
"funding": {
|
|
47
48
|
"type": "individual",
|
|
48
|
-
"url": "
|
|
49
|
+
"url": "https://etherpad.org/"
|
|
49
50
|
},
|
|
50
51
|
"peerDependencies": {
|
|
51
52
|
"ep_etherpad-lite": ">=1.8.6"
|
|
52
53
|
},
|
|
53
54
|
"engines": {
|
|
54
|
-
"node": ">=
|
|
55
|
+
"node": ">=12.13.0"
|
|
55
56
|
}
|
|
56
57
|
}
|
package/static/js/client.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
exports.postToolbarInit = () => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
4
|
+
const {ep_openid_connect: {permit_displayname_change = true} = {}} = clientVars;
|
|
5
|
+
if (!permit_displayname_change) $('#myusernameedit').attr('disabled', true);
|
|
7
6
|
};
|