ep_openid_connect 2.0.6 → 2.1.1

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.
@@ -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/${{github.event.repository.name}}
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/${{github.event.repository.name}}
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
- - run: git config user.name 'github-actions[bot]'
64
- - run: git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
65
- - run: npm ci
66
- - run: npm version patch
67
- - run: git push --follow-tags
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,18 +34,30 @@ Etherpad's `requireAuthentication` setting must be `true`.
34
34
 
35
35
  ### Configuration Details
36
36
 
37
- * `issuer` (required): The base URL of the OpenID Connect identity provider,
38
- used to perform discovery of the endpoints.
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.
42
52
  * `base_url` (required): The public base Etherpad URL. When registering Etherpad
43
53
  with your identity provider, the redirect URL (a.k.a. callback URL) is this
44
54
  base URL plus `/ep_openid_connect/callback`.
45
- * `displayname_claim` (optional; defaults to `"name"`): The claim containing the
46
- name to display in the pad's user list.
55
+ * `displayname_claim` (optional; defaults to `"name"`): The default value of
56
+ `user_properties.displayname.claim`. **Deprecated:** Set
57
+ `user_properties.displayname.claim` instead.
47
58
  * `response_types` (optional; defaults to `["code"]`): List of OpenID Connect
48
59
  response types.
60
+ * `scope` (optional; defaults to `["openid"]`): List of OAuth2 scope strings.
49
61
  * `permit_displayname_change` (optional; defaults to `false`): Whether users may
50
62
  change their displayed name.
51
63
  * `prohibited_usernames` (optional; defaults to `['admin', 'guest']`): List of
@@ -54,6 +66,9 @@ Etherpad's `requireAuthentication` setting must be `true`.
54
66
  the users in the `users` setting and to avoid conflicts with other plugins
55
67
  (such as
56
68
  [ep\_readonly\_guest](https://github.com/ether/ep_readonly_guest#readme)).
69
+ * `user_properties` (optional): Object that controls the automatic creation of
70
+ additional properties on each authenticated user's account object. See below
71
+ for details.
57
72
 
58
73
  ## Interaction with the `users` Setting
59
74
 
@@ -63,15 +78,18 @@ identifier for the user.) Many identity providers (such as GitLab) identify
63
78
  users by a numeric user ID, so the `sub` claim (and thus the Etherpad username)
64
79
  will probably look something like "5374".
65
80
 
66
- All values associated with that username in the `settings.json` `users` object
67
- are applied to the user, including `is_admin`, `readOnly`, and `canCreate`.
68
-
69
- For example, if you want the user identified by "5374" to be a read-only user,
70
- you would add the following to your `settings.json`:
81
+ Each authenticated user gets their own account object. Default properties for a
82
+ user's account object come from the `users` setting in `settings.json`. Etherpad
83
+ uses the `is_admin`, `readOnly`, and `canCreate` properties to control access,
84
+ and this plugin uses the `displayname` property for the name displayed in the
85
+ user list. For example, the following sets the default display name to
86
+ "Firstname Lastname" and the default access to read-only for the user identified
87
+ by "5374":
71
88
 
72
89
  ```json
73
90
  "users": {
74
91
  "5374": {
92
+ "displayname": "Firstname Lastname",
75
93
  "readOnly": true
76
94
  }
77
95
  },
@@ -82,6 +100,54 @@ you can use the `prohibited_usernames` settings to force an authentication error
82
100
  if the `sub` claim happens to match. This is useful for preventing a malicious
83
101
  identity provider from gaining admin access to your Etherpad instance.
84
102
 
103
+ The `user_properties` setting can be used to automatically add or change
104
+ properties on a user's account object when the user authenticates. The
105
+ `user_properties` setting maps a property name to an object that describes how
106
+ the property's value is obtained. For example:
107
+
108
+ ```json
109
+ "ep_openid_connect": {
110
+ "user_properties": {
111
+ "fromClaimWithDefault": {
112
+ "claim": "claimName",
113
+ "default": "default value"
114
+ },
115
+ "fromClaimOrUnset": {
116
+ "claim": "claimName"
117
+ },
118
+ "fixedValue": {
119
+ "default": "fixed value"
120
+ }
121
+ }
122
+ },
123
+ ```
124
+
125
+ The above example sets properties as follows:
126
+ * Each user's `fromClaimWithDefault` property is set to the value of the user's
127
+ `claimName` claim if present, otherwise the property is left unchanged if
128
+ already set, otherwise it is set to the string `"default value"`.
129
+ * Each user's `fromClaimOrUnset` property is set to the value of the user's
130
+ `claimName` claim if present, otherwise the property is left unset/unchanged.
131
+ * Each user's `fixedValue` property is set to the string `"fixed value"`
132
+ unless already set.
133
+
134
+ You can use this feature to control access in the OpenID Connect provider if it
135
+ provides suitable claims:
136
+
137
+ ```json
138
+ "ep_openid_connect": {
139
+ "scope": ["openid", "etherpad"],
140
+ "user_properties": {
141
+ "is_admin": {"claim": "etherpad_is_admin"},
142
+ "readOnly": {"claim": "etherpad_readOnly"},
143
+ "canCreate": {"claim": "etherpad_canCreate"}
144
+ }
145
+ },
146
+ ```
147
+
148
+ To avoid breaking assumptions made by Etherpad, the `username` property cannot
149
+ be altered via the `user_properties` setting.
150
+
85
151
  ## Interaction with the ep\_readonly\_guest Plugin
86
152
 
87
153
  The [ep\_readonly\_guest
package/index.js CHANGED
@@ -5,42 +5,94 @@ const {URL} = require('url');
5
5
  const {Issuer, generators} = require('openid-client');
6
6
  const authorManager = require('ep_etherpad-lite/node/db/AuthorManager');
7
7
 
8
- const pluginName = 'ep_openid_connect';
9
- const logger = log4js.getLogger(pluginName);
8
+ const logger = log4js.getLogger('ep_openid_connect');
10
9
  const settings = {
11
10
  displayname_claim: 'name',
12
11
  response_types: ['code'],
13
12
  permit_displayname_change: false,
14
13
  prohibited_usernames: ['admin', 'guest'],
14
+ scope: ['openid'],
15
+ user_properties: {},
15
16
  };
16
17
  let oidcClient = null;
17
18
 
18
- const ep = (endpoint) => `/${pluginName}/${endpoint}`;
19
+ const ep = (endpoint) => `/ep_openid_connect/${endpoint}`;
19
20
  const endpointUrl = (endpoint) => new URL(ep(endpoint).substr(1), settings.base_url).toString();
20
21
 
21
- exports.loadSettings = async (hookName, {settings: {[pluginName]: config = {}}}) => {
22
+ const discoverIssuer = async (issuerUrl) => {
23
+ issuerUrl = new URL(issuerUrl);
24
+ // https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery says that the URI
25
+ // must not have query or fragment components.
26
+ if (issuerUrl.search) {
27
+ throw new Error(`Unexpected query in issuer URL (${issuerUrl}): ${issuerUrl.search}`);
28
+ }
29
+ if (issuerUrl.hash) {
30
+ throw new Error(`Unexpected fragment in issuer URL (${issuerUrl}): ${issuerUrl.hash}`);
31
+ }
32
+ let issuer;
33
+ try {
34
+ issuer = await Issuer.discover(issuerUrl.href);
35
+ } catch (err) {
36
+ // The URL used to get the issuer metadata doesn't exactly follow RFC 8615; see:
37
+ // https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
38
+ const discoveryUrl = new URL(issuerUrl);
39
+ if (!discoveryUrl.pathname.includes('/.well-known/')) {
40
+ discoveryUrl.pathname =
41
+ `${discoveryUrl.pathname.replace(/\/$/, '')}/.well-known/openid-configuration`;
42
+ }
43
+ logger.error(
44
+ 'Failed to discover issuer metadata via OpenID Connect Discovery ' +
45
+ '(https://openid.net/specs/openid-connect-discovery-1_0.html). ' +
46
+ `Does your issuer support Discovery? (hint: ${discoveryUrl})`);
47
+ throw err;
48
+ }
49
+ logger.info('OpenID Connect Discovery complete.');
50
+ return issuer;
51
+ };
52
+
53
+ const getIssuer = async (settings) => {
54
+ if (settings.issuer) return await discoverIssuer(settings.issuer);
55
+ return new Issuer(settings.issuer_metadata);
56
+ };
57
+
58
+ exports.loadSettings = async (hookName, {settings: {ep_openid_connect: config = {}}}) => {
22
59
  Object.assign(settings, config);
23
- for (const setting of ['base_url', 'client_id', 'client_secret', 'issuer']) {
60
+ for (const setting of ['base_url', 'client_id', 'client_secret']) {
24
61
  if (!settings[setting]) {
25
- logger.error(`Required setting missing from settings.json: ${pluginName}.${setting}`);
62
+ logger.error(`Required setting missing from settings.json: ep_openid_connect.${setting}`);
26
63
  return;
27
64
  }
28
65
  }
66
+ if (!settings.issuer && !settings.issuer_metadata) {
67
+ logger.error(
68
+ 'Either ep_openid_connect.issuer or ep_openid_connect.issuer_metadata must be set');
69
+ return;
70
+ }
71
+ if (settings.issuer && settings.issuer_metadata) {
72
+ logger.warn('Ignoring ep_openid_connect.issuer_metadata setting ' +
73
+ 'because ep_openid_connect.issuer is set');
74
+ }
29
75
  // Make sure base_url ends with '/' so that relative URLs are appended:
30
76
  if (!settings.base_url.endsWith('/')) settings.base_url += '/';
31
- oidcClient = new (await Issuer.discover(settings.issuer)).Client({
77
+ settings.user_properties = {
78
+ displayname: {claim: settings.displayname_claim},
79
+ ...settings.user_properties,
80
+ // The username property must always match the key used in settings.users.
81
+ username: {claim: 'sub'},
82
+ };
83
+ oidcClient = new (await getIssuer(settings)).Client({
32
84
  client_id: settings.client_id,
33
85
  client_secret: settings.client_secret,
34
86
  response_types: settings.response_types,
35
87
  redirect_uris: [endpointUrl('callback')],
36
88
  });
37
- logger.info('Client discovery complete. Configured.');
89
+ logger.info('Configured.');
38
90
  };
39
91
 
40
92
  exports.clientVars = (hookName, context) => {
41
93
  if (oidcClient == null) return;
42
94
  const {permit_displayname_change} = settings;
43
- return {[pluginName]: {permit_displayname_change}};
95
+ return {ep_openid_connect: {permit_displayname_change}};
44
96
  };
45
97
 
46
98
  exports.expressCreateServer = (hookName, {app}) => {
@@ -50,15 +102,11 @@ exports.expressCreateServer = (hookName, {app}) => {
50
102
  logger.debug(`Processing ${req.url}`);
51
103
  (async () => {
52
104
  const params = oidcClient.callbackParams(req);
53
- const oidcSession = req.session[pluginName] || {};
105
+ const oidcSession = req.session.ep_openid_connect || {};
54
106
  const {authParams} = oidcSession;
55
107
  if (authParams == null) throw new Error('no authentication paramters found in session state');
56
108
  const tokenset = await oidcClient.callback(endpointUrl('callback'), params, authParams);
57
- const userinfo = await oidcClient.userinfo(tokenset);
58
- if (settings.prohibited_usernames.indexOf(userinfo.sub) !== -1) {
59
- throw new Error(`authenticated user's 'sub' claim (${userinfo.sub}) is not permitted`);
60
- }
61
- oidcSession.userinfo = userinfo;
109
+ oidcSession.userinfo = await oidcClient.userinfo(tokenset);
62
110
  // The user has successfully authenticated, but don't set req.session.user here -- do it in
63
111
  // the authenticate hook so that Etherpad can log the authentication success. However, DO "log
64
112
  // out" the previous user to force the authenticate hook to run in case the user was already
@@ -73,10 +121,14 @@ exports.expressCreateServer = (hookName, {app}) => {
73
121
  });
74
122
  app.get(ep('login'), (req, res, next) => {
75
123
  logger.debug(`Processing ${req.url}`);
76
- if (req.session[pluginName] == null) req.session[pluginName] = {};
77
- const oidcSession = req.session[pluginName];
124
+ if (req.session.ep_openid_connect == null) req.session.ep_openid_connect = {};
125
+ const oidcSession = req.session.ep_openid_connect;
78
126
  oidcSession.next = req.query.redirect_uri || settings.base_url;
79
- oidcSession.authParams = {nonce: generators.nonce(), state: generators.state()};
127
+ oidcSession.authParams = {
128
+ nonce: generators.nonce(),
129
+ scope: settings.scope.join(' '),
130
+ state: generators.state(),
131
+ };
80
132
  res.redirect(oidcClient.authorizationUrl(oidcSession.authParams));
81
133
  });
82
134
  app.get(ep('logout'), (req, res) => req.session.destroy(() => res.redirect(settings.base_url)));
@@ -86,20 +138,31 @@ exports.authenticate = (hookName, {req, res, users}) => {
86
138
  if (oidcClient == null) return;
87
139
  logger.debug('authenticate hook for', req.url);
88
140
  const {session} = req;
89
- const {[pluginName]: {userinfo = {}} = {}} = session;
141
+ const {ep_openid_connect: {userinfo = {}} = {}} = session;
90
142
  const {sub} = userinfo;
91
- if (sub == null) {
143
+ if (sub == null || // Nullish means the user isn't authenticated.
144
+ typeof sub !== 'string' || // `sub` is used as the username, so it must be a string.
145
+ sub === '' || // Empty string doesn't make sense.
146
+ sub === '__proto__' || // Prevent prototype pollution.
147
+ settings.prohibited_usernames.includes(sub)) {
92
148
  // Out of an abundance of caution, clear out the old state, nonce, and userinfo (if present) to
93
149
  // force regeneration.
94
- delete session[pluginName];
150
+ delete session.ep_openid_connect;
95
151
  // Authn failed. Let another plugin try to authenticate the user.
96
152
  return;
97
153
  }
98
154
  // Successfully authenticated.
99
- if (users[sub] == null) users[sub] = {};
155
+ logger.info('Successfully authenticated user with userinfo:', userinfo);
100
156
  session.user = users[sub];
101
- session.user.username = sub;
102
- session.user.displayname = userinfo[settings.displayname_claim] || session.user.displayname;
157
+ if (session.user == null) session.user = users[sub] = {};
158
+ for (const [propName, descriptor] of Object.entries(settings.user_properties)) {
159
+ if (descriptor.claim != null && descriptor.claim in userinfo) {
160
+ session.user[propName] = userinfo[descriptor.claim];
161
+ } else if ('default' in descriptor && !(propName in session.user)) {
162
+ session.user[propName] = descriptor.default;
163
+ }
164
+ }
165
+ logger.debug('User properties:', session.user);
103
166
  return true;
104
167
  };
105
168
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ep_openid_connect",
3
- "version": "2.0.6",
3
+ "version": "2.1.1",
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": "^3.15.9"
26
+ "openid-client": "^4.7.4"
27
27
  },
28
28
  "devDependencies": {
29
- "eslint": "^7.18.0",
30
- "eslint-config-etherpad": "^1.0.24",
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": "^8.0.0",
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": "^4.2.1",
36
- "eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0"
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": "http://etherpad.org/"
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": ">=10.13.0"
55
+ "node": ">=12.13.0"
55
56
  }
56
57
  }