@beiguancyc/parse-server 9.9.0-ruixiang.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.
- package/LICENSE +176 -0
- package/NOTICE +10 -0
- package/README.md +1311 -0
- package/bin/parse-live-query-server +3 -0
- package/bin/parse-server +3 -0
- package/lib/AccountLockout.js +149 -0
- package/lib/Adapters/AdapterLoader.js +60 -0
- package/lib/Adapters/Analytics/AnalyticsAdapter.js +32 -0
- package/lib/Adapters/Auth/AuthAdapter.js +132 -0
- package/lib/Adapters/Auth/BaseCodeAuthAdapter.js +119 -0
- package/lib/Adapters/Auth/OAuth1Client.js +204 -0
- package/lib/Adapters/Auth/apple.js +125 -0
- package/lib/Adapters/Auth/facebook.js +187 -0
- package/lib/Adapters/Auth/gcenter.js +217 -0
- package/lib/Adapters/Auth/github.js +125 -0
- package/lib/Adapters/Auth/google.js +122 -0
- package/lib/Adapters/Auth/gpgames.js +125 -0
- package/lib/Adapters/Auth/httpsRequest.js +41 -0
- package/lib/Adapters/Auth/index.js +248 -0
- package/lib/Adapters/Auth/instagram.js +118 -0
- package/lib/Adapters/Auth/janraincapture.js +85 -0
- package/lib/Adapters/Auth/janrainengage.js +57 -0
- package/lib/Adapters/Auth/keycloak.js +168 -0
- package/lib/Adapters/Auth/ldap.js +193 -0
- package/lib/Adapters/Auth/line.js +125 -0
- package/lib/Adapters/Auth/linkedin.js +117 -0
- package/lib/Adapters/Auth/meetup.js +44 -0
- package/lib/Adapters/Auth/mfa.js +334 -0
- package/lib/Adapters/Auth/microsoft.js +111 -0
- package/lib/Adapters/Auth/oauth2.js +115 -0
- package/lib/Adapters/Auth/phantauth.js +53 -0
- package/lib/Adapters/Auth/qq.js +113 -0
- package/lib/Adapters/Auth/spotify.js +115 -0
- package/lib/Adapters/Auth/twitter.js +197 -0
- package/lib/Adapters/Auth/utils.js +28 -0
- package/lib/Adapters/Auth/vkontakte.js +55 -0
- package/lib/Adapters/Auth/wechat.js +110 -0
- package/lib/Adapters/Auth/weibo.js +141 -0
- package/lib/Adapters/Cache/CacheAdapter.js +40 -0
- package/lib/Adapters/Cache/InMemoryCache.js +61 -0
- package/lib/Adapters/Cache/InMemoryCacheAdapter.js +34 -0
- package/lib/Adapters/Cache/LRUCache.js +35 -0
- package/lib/Adapters/Cache/NullCacheAdapter.js +26 -0
- package/lib/Adapters/Cache/RedisCacheAdapter.js +109 -0
- package/lib/Adapters/Cache/SchemaCache.js +25 -0
- package/lib/Adapters/Email/MailAdapter.js +32 -0
- package/lib/Adapters/Files/FilesAdapter.js +129 -0
- package/lib/Adapters/Files/GridFSBucketAdapter.js +280 -0
- package/lib/Adapters/Files/GridStoreAdapter.js +5 -0
- package/lib/Adapters/Logger/LoggerAdapter.js +27 -0
- package/lib/Adapters/Logger/WinstonLogger.js +107 -0
- package/lib/Adapters/Logger/WinstonLoggerAdapter.js +63 -0
- package/lib/Adapters/MessageQueue/EventEmitterMQ.js +55 -0
- package/lib/Adapters/PubSub/EventEmitterPubSub.js +53 -0
- package/lib/Adapters/PubSub/PubSubAdapter.js +34 -0
- package/lib/Adapters/PubSub/RedisPubSub.js +49 -0
- package/lib/Adapters/Push/PushAdapter.js +40 -0
- package/lib/Adapters/Storage/Mongo/MongoCollection.js +212 -0
- package/lib/Adapters/Storage/Mongo/MongoSchemaCollection.js +334 -0
- package/lib/Adapters/Storage/Mongo/MongoStorageAdapter.js +1047 -0
- package/lib/Adapters/Storage/Mongo/MongoTransform.js +1397 -0
- package/lib/Adapters/Storage/Postgres/PostgresClient.js +38 -0
- package/lib/Adapters/Storage/Postgres/PostgresConfigParser.js +73 -0
- package/lib/Adapters/Storage/Postgres/PostgresStorageAdapter.js +2409 -0
- package/lib/Adapters/Storage/Postgres/sql/array/add-unique.sql +11 -0
- package/lib/Adapters/Storage/Postgres/sql/array/add.sql +11 -0
- package/lib/Adapters/Storage/Postgres/sql/array/contains-all-regex.sql +14 -0
- package/lib/Adapters/Storage/Postgres/sql/array/contains-all.sql +14 -0
- package/lib/Adapters/Storage/Postgres/sql/array/contains.sql +11 -0
- package/lib/Adapters/Storage/Postgres/sql/array/remove.sql +11 -0
- package/lib/Adapters/Storage/Postgres/sql/index.js +32 -0
- package/lib/Adapters/Storage/Postgres/sql/misc/json-object-set-keys.sql +19 -0
- package/lib/Adapters/Storage/StorageAdapter.js +2 -0
- package/lib/Adapters/WebSocketServer/WSAdapter.js +35 -0
- package/lib/Adapters/WebSocketServer/WSSAdapter.js +66 -0
- package/lib/Auth.js +687 -0
- package/lib/AuthDataLock.js +53 -0
- package/lib/ClientSDK.js +39 -0
- package/lib/Config.js +879 -0
- package/lib/Controllers/AdaptableController.js +72 -0
- package/lib/Controllers/AnalyticsController.js +43 -0
- package/lib/Controllers/CacheController.js +71 -0
- package/lib/Controllers/DatabaseController.js +1685 -0
- package/lib/Controllers/FilesController.js +122 -0
- package/lib/Controllers/HooksController.js +254 -0
- package/lib/Controllers/LiveQueryController.js +72 -0
- package/lib/Controllers/LoggerController.js +222 -0
- package/lib/Controllers/ParseGraphQLController.js +294 -0
- package/lib/Controllers/PushController.js +224 -0
- package/lib/Controllers/SchemaController.js +1563 -0
- package/lib/Controllers/UserController.js +364 -0
- package/lib/Controllers/index.js +251 -0
- package/lib/Controllers/types.js +2 -0
- package/lib/Deprecator/Deprecations.js +100 -0
- package/lib/Deprecator/Deprecator.js +140 -0
- package/lib/Error.js +50 -0
- package/lib/FileUrlValidator.js +69 -0
- package/lib/GraphQL/ParseGraphQLSchema.js +375 -0
- package/lib/GraphQL/ParseGraphQLServer.js +241 -0
- package/lib/GraphQL/helpers/objectsMutations.js +30 -0
- package/lib/GraphQL/helpers/objectsQueries.js +246 -0
- package/lib/GraphQL/helpers/queryComplexity.js +138 -0
- package/lib/GraphQL/loaders/configMutations.js +87 -0
- package/lib/GraphQL/loaders/configQueries.js +79 -0
- package/lib/GraphQL/loaders/defaultGraphQLMutations.js +21 -0
- package/lib/GraphQL/loaders/defaultGraphQLQueries.js +23 -0
- package/lib/GraphQL/loaders/defaultGraphQLTypes.js +1099 -0
- package/lib/GraphQL/loaders/defaultRelaySchema.js +53 -0
- package/lib/GraphQL/loaders/filesMutations.js +107 -0
- package/lib/GraphQL/loaders/functionsMutations.js +77 -0
- package/lib/GraphQL/loaders/parseClassMutations.js +267 -0
- package/lib/GraphQL/loaders/parseClassQueries.js +126 -0
- package/lib/GraphQL/loaders/parseClassTypes.js +493 -0
- package/lib/GraphQL/loaders/schemaDirectives.js +62 -0
- package/lib/GraphQL/loaders/schemaMutations.js +161 -0
- package/lib/GraphQL/loaders/schemaQueries.js +80 -0
- package/lib/GraphQL/loaders/schemaTypes.js +341 -0
- package/lib/GraphQL/loaders/usersMutations.js +433 -0
- package/lib/GraphQL/loaders/usersQueries.js +90 -0
- package/lib/GraphQL/parseGraphQLUtils.js +71 -0
- package/lib/GraphQL/transformers/className.js +14 -0
- package/lib/GraphQL/transformers/constraintType.js +53 -0
- package/lib/GraphQL/transformers/inputType.js +51 -0
- package/lib/GraphQL/transformers/mutation.js +280 -0
- package/lib/GraphQL/transformers/outputType.js +51 -0
- package/lib/GraphQL/transformers/query.js +237 -0
- package/lib/GraphQL/transformers/schemaFields.js +99 -0
- package/lib/InstallationDedup.js +178 -0
- package/lib/KeyPromiseQueue.js +48 -0
- package/lib/LiveQuery/Client.js +90 -0
- package/lib/LiveQuery/Id.js +20 -0
- package/lib/LiveQuery/ParseCloudCodePublisher.js +54 -0
- package/lib/LiveQuery/ParseLiveQueryServer.js +1071 -0
- package/lib/LiveQuery/ParsePubSub.js +37 -0
- package/lib/LiveQuery/ParseWebSocketServer.js +66 -0
- package/lib/LiveQuery/QueryTools.js +443 -0
- package/lib/LiveQuery/RequestSchema.js +161 -0
- package/lib/LiveQuery/SessionTokenCache.js +51 -0
- package/lib/LiveQuery/Subscription.js +47 -0
- package/lib/LiveQuery/equalObjects.js +50 -0
- package/lib/Options/Definitions.js +1599 -0
- package/lib/Options/docs.js +389 -0
- package/lib/Options/index.js +12 -0
- package/lib/Options/parsers.js +89 -0
- package/lib/Page.js +44 -0
- package/lib/ParseMessageQueue.js +24 -0
- package/lib/ParseServer.js +632 -0
- package/lib/ParseServerRESTController.js +173 -0
- package/lib/PromiseRouter.js +208 -0
- package/lib/Push/PushQueue.js +67 -0
- package/lib/Push/PushWorker.js +101 -0
- package/lib/Push/utils.js +129 -0
- package/lib/RestQuery.js +1208 -0
- package/lib/RestWrite.js +1671 -0
- package/lib/Routers/AggregateRouter.js +150 -0
- package/lib/Routers/AnalyticsRouter.js +26 -0
- package/lib/Routers/AudiencesRouter.js +54 -0
- package/lib/Routers/ClassesRouter.js +191 -0
- package/lib/Routers/CloudCodeRouter.js +87 -0
- package/lib/Routers/FeaturesRouter.js +72 -0
- package/lib/Routers/FilesRouter.js +772 -0
- package/lib/Routers/FunctionsRouter.js +341 -0
- package/lib/Routers/GlobalConfigRouter.js +123 -0
- package/lib/Routers/GraphQLRouter.js +41 -0
- package/lib/Routers/HooksRouter.js +121 -0
- package/lib/Routers/IAPValidationRouter.js +113 -0
- package/lib/Routers/InstallationsRouter.js +46 -0
- package/lib/Routers/LogsRouter.js +57 -0
- package/lib/Routers/PagesRouter.js +676 -0
- package/lib/Routers/PurgeRouter.js +45 -0
- package/lib/Routers/PushRouter.js +75 -0
- package/lib/Routers/RolesRouter.js +33 -0
- package/lib/Routers/SchemasRouter.js +118 -0
- package/lib/Routers/SecurityRouter.js +34 -0
- package/lib/Routers/SessionsRouter.js +126 -0
- package/lib/Routers/UsersRouter.js +733 -0
- package/lib/SchemaMigrations/DefinedSchemas.js +379 -0
- package/lib/SchemaMigrations/Migrations.js +30 -0
- package/lib/Security/Check.js +109 -0
- package/lib/Security/CheckGroup.js +44 -0
- package/lib/Security/CheckGroups/CheckGroupDatabase.js +44 -0
- package/lib/Security/CheckGroups/CheckGroupServerConfig.js +160 -0
- package/lib/Security/CheckGroups/CheckGroups.js +21 -0
- package/lib/Security/CheckRunner.js +213 -0
- package/lib/SharedRest.js +34 -0
- package/lib/StatusHandler.js +329 -0
- package/lib/TestUtils.js +87 -0
- package/lib/Utils.js +577 -0
- package/lib/batch.js +185 -0
- package/lib/cache.js +12 -0
- package/lib/cli/definitions/parse-live-query-server.js +9 -0
- package/lib/cli/definitions/parse-server.js +9 -0
- package/lib/cli/parse-live-query-server.js +14 -0
- package/lib/cli/parse-server.js +101 -0
- package/lib/cli/utils/commander.js +132 -0
- package/lib/cli/utils/runner.js +53 -0
- package/lib/cloud-code/Parse.Cloud.js +797 -0
- package/lib/cloud-code/Parse.Server.js +21 -0
- package/lib/cryptoUtils.js +54 -0
- package/lib/defaults.js +58 -0
- package/lib/deprecated.js +12 -0
- package/lib/index.js +89 -0
- package/lib/logger.js +40 -0
- package/lib/middlewares.js +810 -0
- package/lib/password.js +40 -0
- package/lib/request.js +175 -0
- package/lib/requiredParameter.js +11 -0
- package/lib/rest.js +264 -0
- package/lib/triggers.js +949 -0
- package/lib/vendor/README.md +8 -0
- package/lib/vendor/mongodbUrl.js +1012 -0
- package/package.json +172 -0
- package/postinstall.js +38 -0
- package/public/custom_json.html +17 -0
- package/public/custom_json.json +23 -0
- package/public/custom_page.html +15 -0
- package/public/de/email_verification_link_expired.html +24 -0
- package/public/de/email_verification_link_invalid.html +21 -0
- package/public/de/email_verification_send_fail.html +21 -0
- package/public/de/email_verification_send_success.html +19 -0
- package/public/de/email_verification_success.html +18 -0
- package/public/de/password_reset.html +65 -0
- package/public/de/password_reset_link_invalid.html +19 -0
- package/public/de/password_reset_success.html +18 -0
- package/public/de-AT/email_verification_link_expired.html +24 -0
- package/public/de-AT/email_verification_link_invalid.html +21 -0
- package/public/de-AT/email_verification_send_fail.html +21 -0
- package/public/de-AT/email_verification_send_success.html +19 -0
- package/public/de-AT/email_verification_success.html +18 -0
- package/public/de-AT/password_reset.html +65 -0
- package/public/de-AT/password_reset_link_invalid.html +19 -0
- package/public/de-AT/password_reset_success.html +18 -0
- package/public/email_verification_link_expired.html +24 -0
- package/public/email_verification_link_invalid.html +21 -0
- package/public/email_verification_send_fail.html +21 -0
- package/public/email_verification_send_success.html +19 -0
- package/public/email_verification_success.html +18 -0
- package/public/password_reset.html +65 -0
- package/public/password_reset_link_invalid.html +19 -0
- package/public/password_reset_success.html +18 -0
- package/types/@types/@parse/fs-files-adapter/index.d.ts +5 -0
- package/types/@types/deepcopy/index.d.ts +5 -0
- package/types/LiveQuery/ParseLiveQueryServer.d.ts +40 -0
- package/types/Options/index.d.ts +320 -0
- package/types/ParseServer.d.ts +65 -0
- package/types/eslint.config.mjs +30 -0
- package/types/index.d.ts +21 -0
- package/types/logger.d.ts +2 -0
- package/types/tests.ts +44 -0
- package/types/tsconfig.json +24 -0
- package/views/choose_password +215 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse Server authentication adapter for Janrain Capture API.
|
|
5
|
+
*
|
|
6
|
+
* @class JanrainCapture
|
|
7
|
+
* @param {Object} options - The adapter configuration options.
|
|
8
|
+
* @param {String} options.janrain_capture_host - The Janrain Capture API host.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} authData - The authentication data provided by the client.
|
|
11
|
+
* @param {String} authData.id - The Janrain Capture user ID.
|
|
12
|
+
* @param {String} authData.access_token - The Janrain Capture access token.
|
|
13
|
+
*
|
|
14
|
+
* @description
|
|
15
|
+
* ## Parse Server Configuration
|
|
16
|
+
* To configure Parse Server for Janrain Capture authentication, use the following structure:
|
|
17
|
+
* ```json
|
|
18
|
+
* {
|
|
19
|
+
* "auth": {
|
|
20
|
+
* "janrain": {
|
|
21
|
+
* "janrain_capture_host": "your-janrain-capture-host"
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* The adapter requires the following `authData` fields:
|
|
28
|
+
* - `id`: The Janrain Capture user ID.
|
|
29
|
+
* - `access_token`: An authorized Janrain Capture access token for the user.
|
|
30
|
+
*
|
|
31
|
+
* ## Auth Payload Example
|
|
32
|
+
* ```json
|
|
33
|
+
* {
|
|
34
|
+
* "janrain": {
|
|
35
|
+
* "id": "user's Janrain Capture ID as a string",
|
|
36
|
+
* "access_token": "an authorized Janrain Capture access token for the user"
|
|
37
|
+
* }
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* ## Notes
|
|
42
|
+
* Parse Server validates the provided `authData` using the Janrain Capture API.
|
|
43
|
+
*
|
|
44
|
+
* @see {@link https://docs.janrain.com/api/registration/entity/#entity Janrain Capture API Documentation}
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
// Helper functions for accessing the Janrain Capture API.
|
|
48
|
+
var Parse = require('parse/node').Parse;
|
|
49
|
+
var querystring = require('querystring');
|
|
50
|
+
const httpsRequest = require('./httpsRequest');
|
|
51
|
+
|
|
52
|
+
// Returns a promise that fulfills iff this user id is valid.
|
|
53
|
+
function validateAuthData(authData, options) {
|
|
54
|
+
return request(options.janrain_capture_host, authData.access_token).then(data => {
|
|
55
|
+
//successful response will have a "stat" (status) of 'ok' and a result node that stores the uuid, because that's all we asked for
|
|
56
|
+
//see: https://docs.janrain.com/api/registration/entity/#entity
|
|
57
|
+
if (data && data.stat == 'ok' && data.result == authData.id) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Janrain capture auth is invalid for this user.');
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Returns a promise that fulfills iff this app id is valid.
|
|
65
|
+
function validateAppId() {
|
|
66
|
+
//no-op
|
|
67
|
+
return Promise.resolve();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// A promisey wrapper for api requests
|
|
71
|
+
function request(host, access_token) {
|
|
72
|
+
var query_string_data = querystring.stringify({
|
|
73
|
+
access_token: access_token,
|
|
74
|
+
attribute_name: 'uuid' // we only need to pull the uuid for this access token to make sure it matches
|
|
75
|
+
});
|
|
76
|
+
return httpsRequest.get({
|
|
77
|
+
host: host,
|
|
78
|
+
path: '/entity?' + query_string_data
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
module.exports = {
|
|
82
|
+
validateAppId: validateAppId,
|
|
83
|
+
validateAuthData: validateAuthData
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJQYXJzZSIsInJlcXVpcmUiLCJxdWVyeXN0cmluZyIsImh0dHBzUmVxdWVzdCIsInZhbGlkYXRlQXV0aERhdGEiLCJhdXRoRGF0YSIsIm9wdGlvbnMiLCJyZXF1ZXN0IiwiamFucmFpbl9jYXB0dXJlX2hvc3QiLCJhY2Nlc3NfdG9rZW4iLCJ0aGVuIiwiZGF0YSIsInN0YXQiLCJyZXN1bHQiLCJpZCIsIkVycm9yIiwiT0JKRUNUX05PVF9GT1VORCIsInZhbGlkYXRlQXBwSWQiLCJQcm9taXNlIiwicmVzb2x2ZSIsImhvc3QiLCJxdWVyeV9zdHJpbmdfZGF0YSIsInN0cmluZ2lmeSIsImF0dHJpYnV0ZV9uYW1lIiwiZ2V0IiwicGF0aCIsIm1vZHVsZSIsImV4cG9ydHMiXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvQWRhcHRlcnMvQXV0aC9qYW5yYWluY2FwdHVyZS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBhcnNlIFNlcnZlciBhdXRoZW50aWNhdGlvbiBhZGFwdGVyIGZvciBKYW5yYWluIENhcHR1cmUgQVBJLlxuICpcbiAqIEBjbGFzcyBKYW5yYWluQ2FwdHVyZVxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgYWRhcHRlciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gKiBAcGFyYW0ge1N0cmluZ30gb3B0aW9ucy5qYW5yYWluX2NhcHR1cmVfaG9zdCAtIFRoZSBKYW5yYWluIENhcHR1cmUgQVBJIGhvc3QuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IGF1dGhEYXRhIC0gVGhlIGF1dGhlbnRpY2F0aW9uIGRhdGEgcHJvdmlkZWQgYnkgdGhlIGNsaWVudC5cbiAqIEBwYXJhbSB7U3RyaW5nfSBhdXRoRGF0YS5pZCAtIFRoZSBKYW5yYWluIENhcHR1cmUgdXNlciBJRC5cbiAqIEBwYXJhbSB7U3RyaW5nfSBhdXRoRGF0YS5hY2Nlc3NfdG9rZW4gLSBUaGUgSmFucmFpbiBDYXB0dXJlIGFjY2VzcyB0b2tlbi5cbiAqXG4gKiBAZGVzY3JpcHRpb25cbiAqICMjIFBhcnNlIFNlcnZlciBDb25maWd1cmF0aW9uXG4gKiBUbyBjb25maWd1cmUgUGFyc2UgU2VydmVyIGZvciBKYW5yYWluIENhcHR1cmUgYXV0aGVudGljYXRpb24sIHVzZSB0aGUgZm9sbG93aW5nIHN0cnVjdHVyZTpcbiAqIGBgYGpzb25cbiAqIHtcbiAqICAgXCJhdXRoXCI6IHtcbiAqICAgICBcImphbnJhaW5cIjoge1xuICogICAgICAgXCJqYW5yYWluX2NhcHR1cmVfaG9zdFwiOiBcInlvdXItamFucmFpbi1jYXB0dXJlLWhvc3RcIlxuICogICAgIH1cbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogVGhlIGFkYXB0ZXIgcmVxdWlyZXMgdGhlIGZvbGxvd2luZyBgYXV0aERhdGFgIGZpZWxkczpcbiAqIC0gYGlkYDogVGhlIEphbnJhaW4gQ2FwdHVyZSB1c2VyIElELlxuICogLSBgYWNjZXNzX3Rva2VuYDogQW4gYXV0aG9yaXplZCBKYW5yYWluIENhcHR1cmUgYWNjZXNzIHRva2VuIGZvciB0aGUgdXNlci5cbiAqXG4gKiAjIyBBdXRoIFBheWxvYWQgRXhhbXBsZVxuICogYGBganNvblxuICoge1xuICogICBcImphbnJhaW5cIjoge1xuICogICAgIFwiaWRcIjogXCJ1c2VyJ3MgSmFucmFpbiBDYXB0dXJlIElEIGFzIGEgc3RyaW5nXCIsXG4gKiAgICAgXCJhY2Nlc3NfdG9rZW5cIjogXCJhbiBhdXRob3JpemVkIEphbnJhaW4gQ2FwdHVyZSBhY2Nlc3MgdG9rZW4gZm9yIHRoZSB1c2VyXCJcbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogIyMgTm90ZXNcbiAqIFBhcnNlIFNlcnZlciB2YWxpZGF0ZXMgdGhlIHByb3ZpZGVkIGBhdXRoRGF0YWAgdXNpbmcgdGhlIEphbnJhaW4gQ2FwdHVyZSBBUEkuXG4gKlxuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly9kb2NzLmphbnJhaW4uY29tL2FwaS9yZWdpc3RyYXRpb24vZW50aXR5LyNlbnRpdHkgSmFucmFpbiBDYXB0dXJlIEFQSSBEb2N1bWVudGF0aW9ufVxuICovXG5cblxuLy8gSGVscGVyIGZ1bmN0aW9ucyBmb3IgYWNjZXNzaW5nIHRoZSBKYW5yYWluIENhcHR1cmUgQVBJLlxudmFyIFBhcnNlID0gcmVxdWlyZSgncGFyc2Uvbm9kZScpLlBhcnNlO1xudmFyIHF1ZXJ5c3RyaW5nID0gcmVxdWlyZSgncXVlcnlzdHJpbmcnKTtcbmNvbnN0IGh0dHBzUmVxdWVzdCA9IHJlcXVpcmUoJy4vaHR0cHNSZXF1ZXN0Jyk7XG5cbi8vIFJldHVybnMgYSBwcm9taXNlIHRoYXQgZnVsZmlsbHMgaWZmIHRoaXMgdXNlciBpZCBpcyB2YWxpZC5cbmZ1bmN0aW9uIHZhbGlkYXRlQXV0aERhdGEoYXV0aERhdGEsIG9wdGlvbnMpIHtcbiAgcmV0dXJuIHJlcXVlc3Qob3B0aW9ucy5qYW5yYWluX2NhcHR1cmVfaG9zdCwgYXV0aERhdGEuYWNjZXNzX3Rva2VuKS50aGVuKGRhdGEgPT4ge1xuICAgIC8vc3VjY2Vzc2Z1bCByZXNwb25zZSB3aWxsIGhhdmUgYSBcInN0YXRcIiAoc3RhdHVzKSBvZiAnb2snIGFuZCBhIHJlc3VsdCBub2RlIHRoYXQgc3RvcmVzIHRoZSB1dWlkLCBiZWNhdXNlIHRoYXQncyBhbGwgd2UgYXNrZWQgZm9yXG4gICAgLy9zZWU6IGh0dHBzOi8vZG9jcy5qYW5yYWluLmNvbS9hcGkvcmVnaXN0cmF0aW9uL2VudGl0eS8jZW50aXR5XG4gICAgaWYgKGRhdGEgJiYgZGF0YS5zdGF0ID09ICdvaycgJiYgZGF0YS5yZXN1bHQgPT0gYXV0aERhdGEuaWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFxuICAgICAgUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCxcbiAgICAgICdKYW5yYWluIGNhcHR1cmUgYXV0aCBpcyBpbnZhbGlkIGZvciB0aGlzIHVzZXIuJ1xuICAgICk7XG4gIH0pO1xufVxuXG4vLyBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IGZ1bGZpbGxzIGlmZiB0aGlzIGFwcCBpZCBpcyB2YWxpZC5cbmZ1bmN0aW9uIHZhbGlkYXRlQXBwSWQoKSB7XG4gIC8vbm8tb3BcbiAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xufVxuXG4vLyBBIHByb21pc2V5IHdyYXBwZXIgZm9yIGFwaSByZXF1ZXN0c1xuZnVuY3Rpb24gcmVxdWVzdChob3N0LCBhY2Nlc3NfdG9rZW4pIHtcbiAgdmFyIHF1ZXJ5X3N0cmluZ19kYXRhID0gcXVlcnlzdHJpbmcuc3RyaW5naWZ5KHtcbiAgICBhY2Nlc3NfdG9rZW46IGFjY2Vzc190b2tlbixcbiAgICBhdHRyaWJ1dGVfbmFtZTogJ3V1aWQnLCAvLyB3ZSBvbmx5IG5lZWQgdG8gcHVsbCB0aGUgdXVpZCBmb3IgdGhpcyBhY2Nlc3MgdG9rZW4gdG8gbWFrZSBzdXJlIGl0IG1hdGNoZXNcbiAgfSk7XG5cbiAgcmV0dXJuIGh0dHBzUmVxdWVzdC5nZXQoeyBob3N0OiBob3N0LCBwYXRoOiAnL2VudGl0eT8nICsgcXVlcnlfc3RyaW5nX2RhdGEgfSk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICB2YWxpZGF0ZUFwcElkOiB2YWxpZGF0ZUFwcElkLFxuICB2YWxpZGF0ZUF1dGhEYXRhOiB2YWxpZGF0ZUF1dGhEYXRhLFxufTtcbiJdLCJtYXBwaW5ncyI6Ijs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFHQTtBQUNBLElBQUlBLEtBQUssR0FBR0MsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDRCxLQUFLO0FBQ3ZDLElBQUlFLFdBQVcsR0FBR0QsT0FBTyxDQUFDLGFBQWEsQ0FBQztBQUN4QyxNQUFNRSxZQUFZLEdBQUdGLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQzs7QUFFOUM7QUFDQSxTQUFTRyxnQkFBZ0JBLENBQUNDLFFBQVEsRUFBRUMsT0FBTyxFQUFFO0VBQzNDLE9BQU9DLE9BQU8sQ0FBQ0QsT0FBTyxDQUFDRSxvQkFBb0IsRUFBRUgsUUFBUSxDQUFDSSxZQUFZLENBQUMsQ0FBQ0MsSUFBSSxDQUFDQyxJQUFJLElBQUk7SUFDL0U7SUFDQTtJQUNBLElBQUlBLElBQUksSUFBSUEsSUFBSSxDQUFDQyxJQUFJLElBQUksSUFBSSxJQUFJRCxJQUFJLENBQUNFLE1BQU0sSUFBSVIsUUFBUSxDQUFDUyxFQUFFLEVBQUU7TUFDM0Q7SUFDRjtJQUNBLE1BQU0sSUFBSWQsS0FBSyxDQUFDZSxLQUFLLENBQ25CZixLQUFLLENBQUNlLEtBQUssQ0FBQ0MsZ0JBQWdCLEVBQzVCLGdEQUNGLENBQUM7RUFDSCxDQUFDLENBQUM7QUFDSjs7QUFFQTtBQUNBLFNBQVNDLGFBQWFBLENBQUEsRUFBRztFQUN2QjtFQUNBLE9BQU9DLE9BQU8sQ0FBQ0MsT0FBTyxDQUFDLENBQUM7QUFDMUI7O0FBRUE7QUFDQSxTQUFTWixPQUFPQSxDQUFDYSxJQUFJLEVBQUVYLFlBQVksRUFBRTtFQUNuQyxJQUFJWSxpQkFBaUIsR0FBR25CLFdBQVcsQ0FBQ29CLFNBQVMsQ0FBQztJQUM1Q2IsWUFBWSxFQUFFQSxZQUFZO0lBQzFCYyxjQUFjLEVBQUUsTUFBTSxDQUFFO0VBQzFCLENBQUMsQ0FBQztFQUVGLE9BQU9wQixZQUFZLENBQUNxQixHQUFHLENBQUM7SUFBRUosSUFBSSxFQUFFQSxJQUFJO0lBQUVLLElBQUksRUFBRSxVQUFVLEdBQUdKO0VBQWtCLENBQUMsQ0FBQztBQUMvRTtBQUVBSyxNQUFNLENBQUNDLE9BQU8sR0FBRztFQUNmVixhQUFhLEVBQUVBLGFBQWE7RUFDNUJiLGdCQUFnQixFQUFFQTtBQUNwQixDQUFDIiwiaWdub3JlTGlzdCI6W119
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _Config = _interopRequireDefault(require("../../Config"));
|
|
4
|
+
var _Deprecator = _interopRequireDefault(require("../../Deprecator/Deprecator"));
|
|
5
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
// Helper functions for accessing the Janrain Engage API.
|
|
7
|
+
var httpsRequest = require('./httpsRequest');
|
|
8
|
+
var Parse = require('parse/node').Parse;
|
|
9
|
+
var querystring = require('querystring');
|
|
10
|
+
// Returns a promise that fulfills iff this user id is valid.
|
|
11
|
+
function validateAuthData(authData, options) {
|
|
12
|
+
const config = _Config.default.get(Parse.applicationId);
|
|
13
|
+
_Deprecator.default.logRuntimeDeprecation({
|
|
14
|
+
usage: 'janrainengage adapter'
|
|
15
|
+
});
|
|
16
|
+
if (!config?.auth?.janrainengage?.enableInsecureAuth || !config.enableInsecureAuthAdapters) {
|
|
17
|
+
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'janrainengage adapter only works with enableInsecureAuth: true');
|
|
18
|
+
}
|
|
19
|
+
return apiRequest(options.api_key, authData.auth_token).then(data => {
|
|
20
|
+
//successful response will have a "stat" (status) of 'ok' and a profile node with an identifier
|
|
21
|
+
//see: http://developers.janrain.com/overview/social-login/identity-providers/user-profile-data/#normalized-user-profile-data
|
|
22
|
+
if (data && data.stat == 'ok' && data.profile.identifier == authData.id) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Janrain engage auth is invalid for this user.');
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Returns a promise that fulfills iff this app id is valid.
|
|
30
|
+
function validateAppId() {
|
|
31
|
+
//no-op
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// A promisey wrapper for api requests
|
|
36
|
+
function apiRequest(api_key, auth_token) {
|
|
37
|
+
var post_data = querystring.stringify({
|
|
38
|
+
token: auth_token,
|
|
39
|
+
apiKey: api_key,
|
|
40
|
+
format: 'json'
|
|
41
|
+
});
|
|
42
|
+
var post_options = {
|
|
43
|
+
host: 'rpxnow.com',
|
|
44
|
+
path: '/api/v2/auth_info',
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
48
|
+
'Content-Length': post_data.length
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
return httpsRequest.request(post_options, post_data);
|
|
52
|
+
}
|
|
53
|
+
module.exports = {
|
|
54
|
+
validateAppId: validateAppId,
|
|
55
|
+
validateAuthData: validateAuthData
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfQ29uZmlnIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfRGVwcmVjYXRvciIsImUiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsImh0dHBzUmVxdWVzdCIsIlBhcnNlIiwicXVlcnlzdHJpbmciLCJ2YWxpZGF0ZUF1dGhEYXRhIiwiYXV0aERhdGEiLCJvcHRpb25zIiwiY29uZmlnIiwiQ29uZmlnIiwiZ2V0IiwiYXBwbGljYXRpb25JZCIsIkRlcHJlY2F0b3IiLCJsb2dSdW50aW1lRGVwcmVjYXRpb24iLCJ1c2FnZSIsImF1dGgiLCJqYW5yYWluZW5nYWdlIiwiZW5hYmxlSW5zZWN1cmVBdXRoIiwiZW5hYmxlSW5zZWN1cmVBdXRoQWRhcHRlcnMiLCJFcnJvciIsIklOVEVSTkFMX1NFUlZFUl9FUlJPUiIsImFwaVJlcXVlc3QiLCJhcGlfa2V5IiwiYXV0aF90b2tlbiIsInRoZW4iLCJkYXRhIiwic3RhdCIsInByb2ZpbGUiLCJpZGVudGlmaWVyIiwiaWQiLCJPQkpFQ1RfTk9UX0ZPVU5EIiwidmFsaWRhdGVBcHBJZCIsIlByb21pc2UiLCJyZXNvbHZlIiwicG9zdF9kYXRhIiwic3RyaW5naWZ5IiwidG9rZW4iLCJhcGlLZXkiLCJmb3JtYXQiLCJwb3N0X29wdGlvbnMiLCJob3N0IiwicGF0aCIsIm1ldGhvZCIsImhlYWRlcnMiLCJsZW5ndGgiLCJyZXF1ZXN0IiwibW9kdWxlIiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9BZGFwdGVycy9BdXRoL2phbnJhaW5lbmdhZ2UuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gSGVscGVyIGZ1bmN0aW9ucyBmb3IgYWNjZXNzaW5nIHRoZSBKYW5yYWluIEVuZ2FnZSBBUEkuXG52YXIgaHR0cHNSZXF1ZXN0ID0gcmVxdWlyZSgnLi9odHRwc1JlcXVlc3QnKTtcbnZhciBQYXJzZSA9IHJlcXVpcmUoJ3BhcnNlL25vZGUnKS5QYXJzZTtcbnZhciBxdWVyeXN0cmluZyA9IHJlcXVpcmUoJ3F1ZXJ5c3RyaW5nJyk7XG5pbXBvcnQgQ29uZmlnIGZyb20gJy4uLy4uL0NvbmZpZyc7XG5pbXBvcnQgRGVwcmVjYXRvciBmcm9tICcuLi8uLi9EZXByZWNhdG9yL0RlcHJlY2F0b3InO1xuXG4vLyBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IGZ1bGZpbGxzIGlmZiB0aGlzIHVzZXIgaWQgaXMgdmFsaWQuXG5mdW5jdGlvbiB2YWxpZGF0ZUF1dGhEYXRhKGF1dGhEYXRhLCBvcHRpb25zKSB7XG4gIGNvbnN0IGNvbmZpZyA9IENvbmZpZy5nZXQoUGFyc2UuYXBwbGljYXRpb25JZCk7XG5cbiAgRGVwcmVjYXRvci5sb2dSdW50aW1lRGVwcmVjYXRpb24oeyB1c2FnZTogJ2phbnJhaW5lbmdhZ2UgYWRhcHRlcicgfSk7XG4gIGlmICghY29uZmlnPy5hdXRoPy5qYW5yYWluZW5nYWdlPy5lbmFibGVJbnNlY3VyZUF1dGggfHwgIWNvbmZpZy5lbmFibGVJbnNlY3VyZUF1dGhBZGFwdGVycykge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihQYXJzZS5FcnJvci5JTlRFUk5BTF9TRVJWRVJfRVJST1IsICdqYW5yYWluZW5nYWdlIGFkYXB0ZXIgb25seSB3b3JrcyB3aXRoIGVuYWJsZUluc2VjdXJlQXV0aDogdHJ1ZScpO1xuICB9XG5cbiAgcmV0dXJuIGFwaVJlcXVlc3Qob3B0aW9ucy5hcGlfa2V5LCBhdXRoRGF0YS5hdXRoX3Rva2VuKS50aGVuKGRhdGEgPT4ge1xuICAgIC8vc3VjY2Vzc2Z1bCByZXNwb25zZSB3aWxsIGhhdmUgYSBcInN0YXRcIiAoc3RhdHVzKSBvZiAnb2snIGFuZCBhIHByb2ZpbGUgbm9kZSB3aXRoIGFuIGlkZW50aWZpZXJcbiAgICAvL3NlZTogaHR0cDovL2RldmVsb3BlcnMuamFucmFpbi5jb20vb3ZlcnZpZXcvc29jaWFsLWxvZ2luL2lkZW50aXR5LXByb3ZpZGVycy91c2VyLXByb2ZpbGUtZGF0YS8jbm9ybWFsaXplZC11c2VyLXByb2ZpbGUtZGF0YVxuICAgIGlmIChkYXRhICYmIGRhdGEuc3RhdCA9PSAnb2snICYmIGRhdGEucHJvZmlsZS5pZGVudGlmaWVyID09IGF1dGhEYXRhLmlkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihcbiAgICAgIFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsXG4gICAgICAnSmFucmFpbiBlbmdhZ2UgYXV0aCBpcyBpbnZhbGlkIGZvciB0aGlzIHVzZXIuJ1xuICAgICk7XG4gIH0pO1xufVxuXG4vLyBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IGZ1bGZpbGxzIGlmZiB0aGlzIGFwcCBpZCBpcyB2YWxpZC5cbmZ1bmN0aW9uIHZhbGlkYXRlQXBwSWQoKSB7XG4gIC8vbm8tb3BcbiAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xufVxuXG4vLyBBIHByb21pc2V5IHdyYXBwZXIgZm9yIGFwaSByZXF1ZXN0c1xuZnVuY3Rpb24gYXBpUmVxdWVzdChhcGlfa2V5LCBhdXRoX3Rva2VuKSB7XG4gIHZhciBwb3N0X2RhdGEgPSBxdWVyeXN0cmluZy5zdHJpbmdpZnkoe1xuICAgIHRva2VuOiBhdXRoX3Rva2VuLFxuICAgIGFwaUtleTogYXBpX2tleSxcbiAgICBmb3JtYXQ6ICdqc29uJyxcbiAgfSk7XG5cbiAgdmFyIHBvc3Rfb3B0aW9ucyA9IHtcbiAgICBob3N0OiAncnB4bm93LmNvbScsXG4gICAgcGF0aDogJy9hcGkvdjIvYXV0aF9pbmZvJyxcbiAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICBoZWFkZXJzOiB7XG4gICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcsXG4gICAgICAnQ29udGVudC1MZW5ndGgnOiBwb3N0X2RhdGEubGVuZ3RoLFxuICAgIH0sXG4gIH07XG5cbiAgcmV0dXJuIGh0dHBzUmVxdWVzdC5yZXF1ZXN0KHBvc3Rfb3B0aW9ucywgcG9zdF9kYXRhKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIHZhbGlkYXRlQXBwSWQ6IHZhbGlkYXRlQXBwSWQsXG4gIHZhbGlkYXRlQXV0aERhdGE6IHZhbGlkYXRlQXV0aERhdGEsXG59O1xuIl0sIm1hcHBpbmdzIjoiOztBQUlBLElBQUFBLE9BQUEsR0FBQUMsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFDLFdBQUEsR0FBQUYsc0JBQUEsQ0FBQUMsT0FBQTtBQUFxRCxTQUFBRCx1QkFBQUcsQ0FBQSxXQUFBQSxDQUFBLElBQUFBLENBQUEsQ0FBQUMsVUFBQSxHQUFBRCxDQUFBLEtBQUFFLE9BQUEsRUFBQUYsQ0FBQTtBQUxyRDtBQUNBLElBQUlHLFlBQVksR0FBR0wsT0FBTyxDQUFDLGdCQUFnQixDQUFDO0FBQzVDLElBQUlNLEtBQUssR0FBR04sT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDTSxLQUFLO0FBQ3ZDLElBQUlDLFdBQVcsR0FBR1AsT0FBTyxDQUFDLGFBQWEsQ0FBQztBQUl4QztBQUNBLFNBQVNRLGdCQUFnQkEsQ0FBQ0MsUUFBUSxFQUFFQyxPQUFPLEVBQUU7RUFDM0MsTUFBTUMsTUFBTSxHQUFHQyxlQUFNLENBQUNDLEdBQUcsQ0FBQ1AsS0FBSyxDQUFDUSxhQUFhLENBQUM7RUFFOUNDLG1CQUFVLENBQUNDLHFCQUFxQixDQUFDO0lBQUVDLEtBQUssRUFBRTtFQUF3QixDQUFDLENBQUM7RUFDcEUsSUFBSSxDQUFDTixNQUFNLEVBQUVPLElBQUksRUFBRUMsYUFBYSxFQUFFQyxrQkFBa0IsSUFBSSxDQUFDVCxNQUFNLENBQUNVLDBCQUEwQixFQUFFO0lBQzFGLE1BQU0sSUFBSWYsS0FBSyxDQUFDZ0IsS0FBSyxDQUFDaEIsS0FBSyxDQUFDZ0IsS0FBSyxDQUFDQyxxQkFBcUIsRUFBRSxnRUFBZ0UsQ0FBQztFQUM1SDtFQUVBLE9BQU9DLFVBQVUsQ0FBQ2QsT0FBTyxDQUFDZSxPQUFPLEVBQUVoQixRQUFRLENBQUNpQixVQUFVLENBQUMsQ0FBQ0MsSUFBSSxDQUFDQyxJQUFJLElBQUk7SUFDbkU7SUFDQTtJQUNBLElBQUlBLElBQUksSUFBSUEsSUFBSSxDQUFDQyxJQUFJLElBQUksSUFBSSxJQUFJRCxJQUFJLENBQUNFLE9BQU8sQ0FBQ0MsVUFBVSxJQUFJdEIsUUFBUSxDQUFDdUIsRUFBRSxFQUFFO01BQ3ZFO0lBQ0Y7SUFDQSxNQUFNLElBQUkxQixLQUFLLENBQUNnQixLQUFLLENBQ25CaEIsS0FBSyxDQUFDZ0IsS0FBSyxDQUFDVyxnQkFBZ0IsRUFDNUIsK0NBQ0YsQ0FBQztFQUNILENBQUMsQ0FBQztBQUNKOztBQUVBO0FBQ0EsU0FBU0MsYUFBYUEsQ0FBQSxFQUFHO0VBQ3ZCO0VBQ0EsT0FBT0MsT0FBTyxDQUFDQyxPQUFPLENBQUMsQ0FBQztBQUMxQjs7QUFFQTtBQUNBLFNBQVNaLFVBQVVBLENBQUNDLE9BQU8sRUFBRUMsVUFBVSxFQUFFO0VBQ3ZDLElBQUlXLFNBQVMsR0FBRzlCLFdBQVcsQ0FBQytCLFNBQVMsQ0FBQztJQUNwQ0MsS0FBSyxFQUFFYixVQUFVO0lBQ2pCYyxNQUFNLEVBQUVmLE9BQU87SUFDZmdCLE1BQU0sRUFBRTtFQUNWLENBQUMsQ0FBQztFQUVGLElBQUlDLFlBQVksR0FBRztJQUNqQkMsSUFBSSxFQUFFLFlBQVk7SUFDbEJDLElBQUksRUFBRSxtQkFBbUI7SUFDekJDLE1BQU0sRUFBRSxNQUFNO0lBQ2RDLE9BQU8sRUFBRTtNQUNQLGNBQWMsRUFBRSxtQ0FBbUM7TUFDbkQsZ0JBQWdCLEVBQUVULFNBQVMsQ0FBQ1U7SUFDOUI7RUFDRixDQUFDO0VBRUQsT0FBTzFDLFlBQVksQ0FBQzJDLE9BQU8sQ0FBQ04sWUFBWSxFQUFFTCxTQUFTLENBQUM7QUFDdEQ7QUFFQVksTUFBTSxDQUFDQyxPQUFPLEdBQUc7RUFDZmhCLGFBQWEsRUFBRUEsYUFBYTtFQUM1QjFCLGdCQUFnQixFQUFFQTtBQUNwQixDQUFDIiwiaWdub3JlTGlzdCI6W119
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse Server authentication adapter for Keycloak.
|
|
5
|
+
*
|
|
6
|
+
* @class KeycloakAdapter
|
|
7
|
+
* @param {Object} options - The adapter configuration options.
|
|
8
|
+
* @param {Object} options.config - The Keycloak configuration object, typically loaded from a JSON file.
|
|
9
|
+
* @param {String} options.config.auth-server-url - The Keycloak authentication server URL.
|
|
10
|
+
* @param {String} options.config.realm - The Keycloak realm name.
|
|
11
|
+
* @param {String} options.config.client-id - The Keycloak client ID.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} authData - The authentication data provided by the client.
|
|
14
|
+
* @param {String} authData.access_token - The Keycloak access token retrieved during client authentication.
|
|
15
|
+
* @param {String} authData.id - The user ID retrieved from Keycloak during client authentication.
|
|
16
|
+
* @param {Array} [authData.roles] - The roles assigned to the user in Keycloak (optional).
|
|
17
|
+
* @param {Array} [authData.groups] - The groups assigned to the user in Keycloak (optional).
|
|
18
|
+
*
|
|
19
|
+
* @description
|
|
20
|
+
* ## Parse Server Configuration
|
|
21
|
+
* To configure Parse Server for Keycloak authentication, use the following structure:
|
|
22
|
+
* ```javascript
|
|
23
|
+
* {
|
|
24
|
+
* "auth": {
|
|
25
|
+
* "keycloak": {
|
|
26
|
+
* "config": require('./auth/keycloak.json')
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
* Ensure the `keycloak.json` configuration file is generated from Keycloak's setup guide and includes:
|
|
32
|
+
* - `auth-server-url`: The Keycloak authentication server URL.
|
|
33
|
+
* - `realm`: The Keycloak realm name.
|
|
34
|
+
* - `client-id`: The Keycloak client ID.
|
|
35
|
+
*
|
|
36
|
+
* ## Auth Data
|
|
37
|
+
* The adapter requires the following `authData` fields:
|
|
38
|
+
* - `access_token`: The Keycloak access token retrieved during client authentication.
|
|
39
|
+
* - `id`: The user ID retrieved from Keycloak during client authentication.
|
|
40
|
+
* - `roles` (optional): The roles assigned to the user in Keycloak.
|
|
41
|
+
* - `groups` (optional): The groups assigned to the user in Keycloak.
|
|
42
|
+
*
|
|
43
|
+
* ## Auth Payload Example
|
|
44
|
+
* ### Example Auth Data
|
|
45
|
+
* ```json
|
|
46
|
+
* {
|
|
47
|
+
* "keycloak": {
|
|
48
|
+
* "access_token": "an authorized Keycloak access token for the user",
|
|
49
|
+
* "id": "user's Keycloak ID as a string",
|
|
50
|
+
* "roles": ["admin", "user"],
|
|
51
|
+
* "groups": ["group1", "group2"]
|
|
52
|
+
* }
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* ## Notes
|
|
57
|
+
* - Parse Server validates the provided `authData` by making a `userinfo` call to Keycloak and ensures the attributes match those returned by Keycloak.
|
|
58
|
+
*
|
|
59
|
+
* ## Keycloak Configuration
|
|
60
|
+
* To configure Keycloak, copy the JSON configuration file generated from Keycloak's setup guide:
|
|
61
|
+
* - [Keycloak Securing Apps Documentation](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter)
|
|
62
|
+
*
|
|
63
|
+
* Place the configuration file on your server, for example:
|
|
64
|
+
* - `auth/keycloak.json`
|
|
65
|
+
*
|
|
66
|
+
* For more information on Keycloak authentication, see:
|
|
67
|
+
* - [Securing Apps Documentation](https://www.keycloak.org/docs/latest/securing_apps/)
|
|
68
|
+
* - [Server Administration Documentation](https://www.keycloak.org/docs/latest/server_admin/)
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
const {
|
|
72
|
+
Parse
|
|
73
|
+
} = require('parse/node');
|
|
74
|
+
const jwksClient = require('jwks-rsa');
|
|
75
|
+
const jwt = require('jsonwebtoken');
|
|
76
|
+
const authUtils = require('./utils');
|
|
77
|
+
const arraysEqual = (_arr1, _arr2) => {
|
|
78
|
+
if (!Array.isArray(_arr1) || !Array.isArray(_arr2) || _arr1.length !== _arr2.length) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
var arr1 = _arr1.concat().sort();
|
|
82
|
+
var arr2 = _arr2.concat().sort();
|
|
83
|
+
for (var i = 0; i < arr1.length; i++) {
|
|
84
|
+
if (arr1[i] !== arr2[i]) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
};
|
|
90
|
+
const getKeycloakKeyByKeyId = async (keyId, jwksUri, cacheMaxEntries, cacheMaxAge) => {
|
|
91
|
+
const client = jwksClient({
|
|
92
|
+
jwksUri,
|
|
93
|
+
cache: true,
|
|
94
|
+
cacheMaxEntries,
|
|
95
|
+
cacheMaxAge
|
|
96
|
+
});
|
|
97
|
+
let key;
|
|
98
|
+
try {
|
|
99
|
+
key = await authUtils.getSigningKey(client, keyId);
|
|
100
|
+
} catch {
|
|
101
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `Unable to find matching key for Key ID: ${keyId}`);
|
|
102
|
+
}
|
|
103
|
+
return key;
|
|
104
|
+
};
|
|
105
|
+
const verifyAccessToken = async ({
|
|
106
|
+
access_token,
|
|
107
|
+
id,
|
|
108
|
+
roles,
|
|
109
|
+
groups
|
|
110
|
+
} = {}, {
|
|
111
|
+
config,
|
|
112
|
+
cacheMaxEntries,
|
|
113
|
+
cacheMaxAge
|
|
114
|
+
} = {}) => {
|
|
115
|
+
if (!(access_token && id)) {
|
|
116
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Missing access token and/or User id');
|
|
117
|
+
}
|
|
118
|
+
if (!config || !(config['auth-server-url'] && config['realm'])) {
|
|
119
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Missing keycloak configuration');
|
|
120
|
+
}
|
|
121
|
+
if (!config['client-id']) {
|
|
122
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Keycloak auth is not configured. Missing client-id.');
|
|
123
|
+
}
|
|
124
|
+
const expectedIssuer = `${config['auth-server-url']}/realms/${config['realm']}`;
|
|
125
|
+
const jwksUri = `${config['auth-server-url']}/realms/${config['realm']}/protocol/openid-connect/certs`;
|
|
126
|
+
const {
|
|
127
|
+
kid: keyId
|
|
128
|
+
} = authUtils.getHeaderFromToken(access_token);
|
|
129
|
+
const ONE_HOUR_IN_MS = 3600000;
|
|
130
|
+
cacheMaxAge = cacheMaxAge || ONE_HOUR_IN_MS;
|
|
131
|
+
cacheMaxEntries = cacheMaxEntries || 5;
|
|
132
|
+
const keycloakKey = await getKeycloakKeyByKeyId(keyId, jwksUri, cacheMaxEntries, cacheMaxAge);
|
|
133
|
+
const signingKey = keycloakKey.publicKey || keycloakKey.rsaPublicKey;
|
|
134
|
+
let jwtClaims;
|
|
135
|
+
try {
|
|
136
|
+
jwtClaims = jwt.verify(access_token, signingKey, {
|
|
137
|
+
algorithms: ['RS256']
|
|
138
|
+
});
|
|
139
|
+
} catch (exception) {
|
|
140
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${exception.message}`);
|
|
141
|
+
}
|
|
142
|
+
if (jwtClaims.iss !== expectedIssuer) {
|
|
143
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `access token not issued by correct provider - expected: ${expectedIssuer} | from: ${jwtClaims.iss}`);
|
|
144
|
+
}
|
|
145
|
+
if (jwtClaims.azp !== config['client-id']) {
|
|
146
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `access token is not authorized for this client - expected: ${config['client-id']} | from: ${jwtClaims.azp}`);
|
|
147
|
+
}
|
|
148
|
+
if (jwtClaims.sub !== id) {
|
|
149
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'auth data is invalid for this user.');
|
|
150
|
+
}
|
|
151
|
+
const rolesMatch = jwtClaims.roles === roles || arraysEqual(jwtClaims.roles, roles);
|
|
152
|
+
const groupsMatch = jwtClaims.groups === groups || arraysEqual(jwtClaims.groups, groups);
|
|
153
|
+
if (!rolesMatch || !groupsMatch) {
|
|
154
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid authentication');
|
|
155
|
+
}
|
|
156
|
+
return jwtClaims;
|
|
157
|
+
};
|
|
158
|
+
function validateAuthData(authData, options = {}) {
|
|
159
|
+
return verifyAccessToken(authData, options);
|
|
160
|
+
}
|
|
161
|
+
function validateAppId() {
|
|
162
|
+
return Promise.resolve();
|
|
163
|
+
}
|
|
164
|
+
module.exports = {
|
|
165
|
+
validateAppId,
|
|
166
|
+
validateAuthData
|
|
167
|
+
};
|
|
168
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJQYXJzZSIsInJlcXVpcmUiLCJqd2tzQ2xpZW50Iiwiand0IiwiYXV0aFV0aWxzIiwiYXJyYXlzRXF1YWwiLCJfYXJyMSIsIl9hcnIyIiwiQXJyYXkiLCJpc0FycmF5IiwibGVuZ3RoIiwiYXJyMSIsImNvbmNhdCIsInNvcnQiLCJhcnIyIiwiaSIsImdldEtleWNsb2FrS2V5QnlLZXlJZCIsImtleUlkIiwiandrc1VyaSIsImNhY2hlTWF4RW50cmllcyIsImNhY2hlTWF4QWdlIiwiY2xpZW50IiwiY2FjaGUiLCJrZXkiLCJnZXRTaWduaW5nS2V5IiwiRXJyb3IiLCJPQkpFQ1RfTk9UX0ZPVU5EIiwidmVyaWZ5QWNjZXNzVG9rZW4iLCJhY2Nlc3NfdG9rZW4iLCJpZCIsInJvbGVzIiwiZ3JvdXBzIiwiY29uZmlnIiwiZXhwZWN0ZWRJc3N1ZXIiLCJraWQiLCJnZXRIZWFkZXJGcm9tVG9rZW4iLCJPTkVfSE9VUl9JTl9NUyIsImtleWNsb2FrS2V5Iiwic2lnbmluZ0tleSIsInB1YmxpY0tleSIsInJzYVB1YmxpY0tleSIsImp3dENsYWltcyIsInZlcmlmeSIsImFsZ29yaXRobXMiLCJleGNlcHRpb24iLCJtZXNzYWdlIiwiaXNzIiwiYXpwIiwic3ViIiwicm9sZXNNYXRjaCIsImdyb3Vwc01hdGNoIiwidmFsaWRhdGVBdXRoRGF0YSIsImF1dGhEYXRhIiwib3B0aW9ucyIsInZhbGlkYXRlQXBwSWQiLCJQcm9taXNlIiwicmVzb2x2ZSIsIm1vZHVsZSIsImV4cG9ydHMiXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvQWRhcHRlcnMvQXV0aC9rZXljbG9hay5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBhcnNlIFNlcnZlciBhdXRoZW50aWNhdGlvbiBhZGFwdGVyIGZvciBLZXljbG9hay5cbiAqXG4gKiBAY2xhc3MgS2V5Y2xvYWtBZGFwdGVyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBhZGFwdGVyIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zLmNvbmZpZyAtIFRoZSBLZXljbG9hayBjb25maWd1cmF0aW9uIG9iamVjdCwgdHlwaWNhbGx5IGxvYWRlZCBmcm9tIGEgSlNPTiBmaWxlLlxuICogQHBhcmFtIHtTdHJpbmd9IG9wdGlvbnMuY29uZmlnLmF1dGgtc2VydmVyLXVybCAtIFRoZSBLZXljbG9hayBhdXRoZW50aWNhdGlvbiBzZXJ2ZXIgVVJMLlxuICogQHBhcmFtIHtTdHJpbmd9IG9wdGlvbnMuY29uZmlnLnJlYWxtIC0gVGhlIEtleWNsb2FrIHJlYWxtIG5hbWUuXG4gKiBAcGFyYW0ge1N0cmluZ30gb3B0aW9ucy5jb25maWcuY2xpZW50LWlkIC0gVGhlIEtleWNsb2FrIGNsaWVudCBJRC5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gYXV0aERhdGEgLSBUaGUgYXV0aGVudGljYXRpb24gZGF0YSBwcm92aWRlZCBieSB0aGUgY2xpZW50LlxuICogQHBhcmFtIHtTdHJpbmd9IGF1dGhEYXRhLmFjY2Vzc190b2tlbiAtIFRoZSBLZXljbG9hayBhY2Nlc3MgdG9rZW4gcmV0cmlldmVkIGR1cmluZyBjbGllbnQgYXV0aGVudGljYXRpb24uXG4gKiBAcGFyYW0ge1N0cmluZ30gYXV0aERhdGEuaWQgLSBUaGUgdXNlciBJRCByZXRyaWV2ZWQgZnJvbSBLZXljbG9hayBkdXJpbmcgY2xpZW50IGF1dGhlbnRpY2F0aW9uLlxuICogQHBhcmFtIHtBcnJheX0gW2F1dGhEYXRhLnJvbGVzXSAtIFRoZSByb2xlcyBhc3NpZ25lZCB0byB0aGUgdXNlciBpbiBLZXljbG9hayAob3B0aW9uYWwpLlxuICogQHBhcmFtIHtBcnJheX0gW2F1dGhEYXRhLmdyb3Vwc10gLSBUaGUgZ3JvdXBzIGFzc2lnbmVkIHRvIHRoZSB1c2VyIGluIEtleWNsb2FrIChvcHRpb25hbCkuXG4gKlxuICogQGRlc2NyaXB0aW9uXG4gKiAjIyBQYXJzZSBTZXJ2ZXIgQ29uZmlndXJhdGlvblxuICogVG8gY29uZmlndXJlIFBhcnNlIFNlcnZlciBmb3IgS2V5Y2xvYWsgYXV0aGVudGljYXRpb24sIHVzZSB0aGUgZm9sbG93aW5nIHN0cnVjdHVyZTpcbiAqIGBgYGphdmFzY3JpcHRcbiAqIHtcbiAqICAgXCJhdXRoXCI6IHtcbiAqICAgICBcImtleWNsb2FrXCI6IHtcbiAqICAgICAgIFwiY29uZmlnXCI6IHJlcXVpcmUoJy4vYXV0aC9rZXljbG9hay5qc29uJylcbiAqICAgICB9XG4gKiAgIH1cbiAqIH1cbiAqIGBgYFxuICogRW5zdXJlIHRoZSBga2V5Y2xvYWsuanNvbmAgY29uZmlndXJhdGlvbiBmaWxlIGlzIGdlbmVyYXRlZCBmcm9tIEtleWNsb2FrJ3Mgc2V0dXAgZ3VpZGUgYW5kIGluY2x1ZGVzOlxuICogLSBgYXV0aC1zZXJ2ZXItdXJsYDogVGhlIEtleWNsb2FrIGF1dGhlbnRpY2F0aW9uIHNlcnZlciBVUkwuXG4gKiAtIGByZWFsbWA6IFRoZSBLZXljbG9hayByZWFsbSBuYW1lLlxuICogLSBgY2xpZW50LWlkYDogVGhlIEtleWNsb2FrIGNsaWVudCBJRC5cbiAqXG4gKiAjIyBBdXRoIERhdGFcbiAqIFRoZSBhZGFwdGVyIHJlcXVpcmVzIHRoZSBmb2xsb3dpbmcgYGF1dGhEYXRhYCBmaWVsZHM6XG4gKiAtIGBhY2Nlc3NfdG9rZW5gOiBUaGUgS2V5Y2xvYWsgYWNjZXNzIHRva2VuIHJldHJpZXZlZCBkdXJpbmcgY2xpZW50IGF1dGhlbnRpY2F0aW9uLlxuICogLSBgaWRgOiBUaGUgdXNlciBJRCByZXRyaWV2ZWQgZnJvbSBLZXljbG9hayBkdXJpbmcgY2xpZW50IGF1dGhlbnRpY2F0aW9uLlxuICogLSBgcm9sZXNgIChvcHRpb25hbCk6IFRoZSByb2xlcyBhc3NpZ25lZCB0byB0aGUgdXNlciBpbiBLZXljbG9hay5cbiAqIC0gYGdyb3Vwc2AgKG9wdGlvbmFsKTogVGhlIGdyb3VwcyBhc3NpZ25lZCB0byB0aGUgdXNlciBpbiBLZXljbG9hay5cbiAqXG4gKiAjIyBBdXRoIFBheWxvYWQgRXhhbXBsZVxuICogIyMjIEV4YW1wbGUgQXV0aCBEYXRhXG4gKiBgYGBqc29uXG4gKiB7XG4gKiAgIFwia2V5Y2xvYWtcIjoge1xuICogICAgIFwiYWNjZXNzX3Rva2VuXCI6IFwiYW4gYXV0aG9yaXplZCBLZXljbG9hayBhY2Nlc3MgdG9rZW4gZm9yIHRoZSB1c2VyXCIsXG4gKiAgICAgXCJpZFwiOiBcInVzZXIncyBLZXljbG9hayBJRCBhcyBhIHN0cmluZ1wiLFxuICogICAgIFwicm9sZXNcIjogW1wiYWRtaW5cIiwgXCJ1c2VyXCJdLFxuICogICAgIFwiZ3JvdXBzXCI6IFtcImdyb3VwMVwiLCBcImdyb3VwMlwiXVxuICogICB9XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiAjIyBOb3Rlc1xuICogLSBQYXJzZSBTZXJ2ZXIgdmFsaWRhdGVzIHRoZSBwcm92aWRlZCBgYXV0aERhdGFgIGJ5IG1ha2luZyBhIGB1c2VyaW5mb2AgY2FsbCB0byBLZXljbG9hayBhbmQgZW5zdXJlcyB0aGUgYXR0cmlidXRlcyBtYXRjaCB0aG9zZSByZXR1cm5lZCBieSBLZXljbG9hay5cbiAqXG4gKiAjIyBLZXljbG9hayBDb25maWd1cmF0aW9uXG4gKiBUbyBjb25maWd1cmUgS2V5Y2xvYWssIGNvcHkgdGhlIEpTT04gY29uZmlndXJhdGlvbiBmaWxlIGdlbmVyYXRlZCBmcm9tIEtleWNsb2FrJ3Mgc2V0dXAgZ3VpZGU6XG4gKiAtIFtLZXljbG9hayBTZWN1cmluZyBBcHBzIERvY3VtZW50YXRpb25dKGh0dHBzOi8vd3d3LmtleWNsb2FrLm9yZy9kb2NzL2xhdGVzdC9zZWN1cmluZ19hcHBzL2luZGV4Lmh0bWwjX2phdmFzY3JpcHRfYWRhcHRlcilcbiAqXG4gKiBQbGFjZSB0aGUgY29uZmlndXJhdGlvbiBmaWxlIG9uIHlvdXIgc2VydmVyLCBmb3IgZXhhbXBsZTpcbiAqIC0gYGF1dGgva2V5Y2xvYWsuanNvbmBcbiAqXG4gKiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiBLZXljbG9hayBhdXRoZW50aWNhdGlvbiwgc2VlOlxuICogLSBbU2VjdXJpbmcgQXBwcyBEb2N1bWVudGF0aW9uXShodHRwczovL3d3dy5rZXljbG9hay5vcmcvZG9jcy9sYXRlc3Qvc2VjdXJpbmdfYXBwcy8pXG4gKiAtIFtTZXJ2ZXIgQWRtaW5pc3RyYXRpb24gRG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly93d3cua2V5Y2xvYWsub3JnL2RvY3MvbGF0ZXN0L3NlcnZlcl9hZG1pbi8pXG4gKi9cblxuY29uc3QgeyBQYXJzZSB9ID0gcmVxdWlyZSgncGFyc2Uvbm9kZScpO1xuY29uc3Qgandrc0NsaWVudCA9IHJlcXVpcmUoJ2p3a3MtcnNhJyk7XG5jb25zdCBqd3QgPSByZXF1aXJlKCdqc29ud2VidG9rZW4nKTtcbmNvbnN0IGF1dGhVdGlscyA9IHJlcXVpcmUoJy4vdXRpbHMnKTtcblxuY29uc3QgYXJyYXlzRXF1YWwgPSAoX2FycjEsIF9hcnIyKSA9PiB7XG4gIGlmICghQXJyYXkuaXNBcnJheShfYXJyMSkgfHwgIUFycmF5LmlzQXJyYXkoX2FycjIpIHx8IF9hcnIxLmxlbmd0aCAhPT0gX2FycjIubGVuZ3RoKSB7IHJldHVybiBmYWxzZTsgfVxuXG4gIHZhciBhcnIxID0gX2FycjEuY29uY2F0KCkuc29ydCgpO1xuICB2YXIgYXJyMiA9IF9hcnIyLmNvbmNhdCgpLnNvcnQoKTtcblxuICBmb3IgKHZhciBpID0gMDsgaSA8IGFycjEubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAoYXJyMVtpXSAhPT0gYXJyMltpXSkgeyByZXR1cm4gZmFsc2U7IH1cbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufTtcblxuY29uc3QgZ2V0S2V5Y2xvYWtLZXlCeUtleUlkID0gYXN5bmMgKGtleUlkLCBqd2tzVXJpLCBjYWNoZU1heEVudHJpZXMsIGNhY2hlTWF4QWdlKSA9PiB7XG4gIGNvbnN0IGNsaWVudCA9IGp3a3NDbGllbnQoe1xuICAgIGp3a3NVcmksXG4gICAgY2FjaGU6IHRydWUsXG4gICAgY2FjaGVNYXhFbnRyaWVzLFxuICAgIGNhY2hlTWF4QWdlLFxuICB9KTtcblxuICBsZXQga2V5O1xuICB0cnkge1xuICAgIGtleSA9IGF3YWl0IGF1dGhVdGlscy5nZXRTaWduaW5nS2V5KGNsaWVudCwga2V5SWQpO1xuICB9IGNhdGNoIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoXG4gICAgICBQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELFxuICAgICAgYFVuYWJsZSB0byBmaW5kIG1hdGNoaW5nIGtleSBmb3IgS2V5IElEOiAke2tleUlkfWBcbiAgICApO1xuICB9XG4gIHJldHVybiBrZXk7XG59O1xuXG5jb25zdCB2ZXJpZnlBY2Nlc3NUb2tlbiA9IGFzeW5jIChcbiAgeyBhY2Nlc3NfdG9rZW4sIGlkLCByb2xlcywgZ3JvdXBzIH0gPSB7fSxcbiAgeyBjb25maWcsIGNhY2hlTWF4RW50cmllcywgY2FjaGVNYXhBZ2UgfSA9IHt9XG4pID0+IHtcbiAgaWYgKCEoYWNjZXNzX3Rva2VuICYmIGlkKSkge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELCAnTWlzc2luZyBhY2Nlc3MgdG9rZW4gYW5kL29yIFVzZXIgaWQnKTtcbiAgfVxuICBpZiAoIWNvbmZpZyB8fCAhKGNvbmZpZ1snYXV0aC1zZXJ2ZXItdXJsJ10gJiYgY29uZmlnWydyZWFsbSddKSkge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELCAnTWlzc2luZyBrZXljbG9hayBjb25maWd1cmF0aW9uJyk7XG4gIH1cbiAgaWYgKCFjb25maWdbJ2NsaWVudC1pZCddKSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFxuICAgICAgUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCxcbiAgICAgICdLZXljbG9hayBhdXRoIGlzIG5vdCBjb25maWd1cmVkLiBNaXNzaW5nIGNsaWVudC1pZC4nXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IGV4cGVjdGVkSXNzdWVyID0gYCR7Y29uZmlnWydhdXRoLXNlcnZlci11cmwnXX0vcmVhbG1zLyR7Y29uZmlnWydyZWFsbSddfWA7XG4gIGNvbnN0IGp3a3NVcmkgPSBgJHtjb25maWdbJ2F1dGgtc2VydmVyLXVybCddfS9yZWFsbXMvJHtjb25maWdbJ3JlYWxtJ119L3Byb3RvY29sL29wZW5pZC1jb25uZWN0L2NlcnRzYDtcblxuICBjb25zdCB7IGtpZDoga2V5SWQgfSA9IGF1dGhVdGlscy5nZXRIZWFkZXJGcm9tVG9rZW4oYWNjZXNzX3Rva2VuKTtcbiAgY29uc3QgT05FX0hPVVJfSU5fTVMgPSAzNjAwMDAwO1xuXG4gIGNhY2hlTWF4QWdlID0gY2FjaGVNYXhBZ2UgfHwgT05FX0hPVVJfSU5fTVM7XG4gIGNhY2hlTWF4RW50cmllcyA9IGNhY2hlTWF4RW50cmllcyB8fCA1O1xuXG4gIGNvbnN0IGtleWNsb2FrS2V5ID0gYXdhaXQgZ2V0S2V5Y2xvYWtLZXlCeUtleUlkKGtleUlkLCBqd2tzVXJpLCBjYWNoZU1heEVudHJpZXMsIGNhY2hlTWF4QWdlKTtcbiAgY29uc3Qgc2lnbmluZ0tleSA9IGtleWNsb2FrS2V5LnB1YmxpY0tleSB8fCBrZXljbG9ha0tleS5yc2FQdWJsaWNLZXk7XG5cbiAgbGV0IGp3dENsYWltcztcbiAgdHJ5IHtcbiAgICBqd3RDbGFpbXMgPSBqd3QudmVyaWZ5KGFjY2Vzc190b2tlbiwgc2lnbmluZ0tleSwge1xuICAgICAgYWxnb3JpdGhtczogWydSUzI1NiddLFxuICAgIH0pO1xuICB9IGNhdGNoIChleGNlcHRpb24pIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCwgYCR7ZXhjZXB0aW9uLm1lc3NhZ2V9YCk7XG4gIH1cblxuICBpZiAoand0Q2xhaW1zLmlzcyAhPT0gZXhwZWN0ZWRJc3N1ZXIpIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoXG4gICAgICBQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELFxuICAgICAgYGFjY2VzcyB0b2tlbiBub3QgaXNzdWVkIGJ5IGNvcnJlY3QgcHJvdmlkZXIgLSBleHBlY3RlZDogJHtleHBlY3RlZElzc3Vlcn0gfCBmcm9tOiAke2p3dENsYWltcy5pc3N9YFxuICAgICk7XG4gIH1cblxuICBpZiAoand0Q2xhaW1zLmF6cCAhPT0gY29uZmlnWydjbGllbnQtaWQnXSkge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihcbiAgICAgIFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsXG4gICAgICBgYWNjZXNzIHRva2VuIGlzIG5vdCBhdXRob3JpemVkIGZvciB0aGlzIGNsaWVudCAtIGV4cGVjdGVkOiAke2NvbmZpZ1snY2xpZW50LWlkJ119IHwgZnJvbTogJHtqd3RDbGFpbXMuYXpwfWBcbiAgICApO1xuICB9XG5cbiAgaWYgKGp3dENsYWltcy5zdWIgIT09IGlkKSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsICdhdXRoIGRhdGEgaXMgaW52YWxpZCBmb3IgdGhpcyB1c2VyLicpO1xuICB9XG5cbiAgY29uc3Qgcm9sZXNNYXRjaCA9IGp3dENsYWltcy5yb2xlcyA9PT0gcm9sZXMgfHwgYXJyYXlzRXF1YWwoand0Q2xhaW1zLnJvbGVzLCByb2xlcyk7XG4gIGNvbnN0IGdyb3Vwc01hdGNoID0gand0Q2xhaW1zLmdyb3VwcyA9PT0gZ3JvdXBzIHx8IGFycmF5c0VxdWFsKGp3dENsYWltcy5ncm91cHMsIGdyb3Vwcyk7XG5cbiAgaWYgKCFyb2xlc01hdGNoIHx8ICFncm91cHNNYXRjaCkge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELCAnSW52YWxpZCBhdXRoZW50aWNhdGlvbicpO1xuICB9XG5cbiAgcmV0dXJuIGp3dENsYWltcztcbn07XG5cbmZ1bmN0aW9uIHZhbGlkYXRlQXV0aERhdGEoYXV0aERhdGEsIG9wdGlvbnMgPSB7fSkge1xuICByZXR1cm4gdmVyaWZ5QWNjZXNzVG9rZW4oYXV0aERhdGEsIG9wdGlvbnMpO1xufVxuXG5mdW5jdGlvbiB2YWxpZGF0ZUFwcElkKCkge1xuICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICB2YWxpZGF0ZUFwcElkLFxuICB2YWxpZGF0ZUF1dGhEYXRhLFxufTtcbiJdLCJtYXBwaW5ncyI6Ijs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxNQUFNO0VBQUVBO0FBQU0sQ0FBQyxHQUFHQyxPQUFPLENBQUMsWUFBWSxDQUFDO0FBQ3ZDLE1BQU1DLFVBQVUsR0FBR0QsT0FBTyxDQUFDLFVBQVUsQ0FBQztBQUN0QyxNQUFNRSxHQUFHLEdBQUdGLE9BQU8sQ0FBQyxjQUFjLENBQUM7QUFDbkMsTUFBTUcsU0FBUyxHQUFHSCxPQUFPLENBQUMsU0FBUyxDQUFDO0FBRXBDLE1BQU1JLFdBQVcsR0FBR0EsQ0FBQ0MsS0FBSyxFQUFFQyxLQUFLLEtBQUs7RUFDcEMsSUFBSSxDQUFDQyxLQUFLLENBQUNDLE9BQU8sQ0FBQ0gsS0FBSyxDQUFDLElBQUksQ0FBQ0UsS0FBSyxDQUFDQyxPQUFPLENBQUNGLEtBQUssQ0FBQyxJQUFJRCxLQUFLLENBQUNJLE1BQU0sS0FBS0gsS0FBSyxDQUFDRyxNQUFNLEVBQUU7SUFBRSxPQUFPLEtBQUs7RUFBRTtFQUVyRyxJQUFJQyxJQUFJLEdBQUdMLEtBQUss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
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse Server authentication adapter for LDAP.
|
|
5
|
+
*
|
|
6
|
+
* @class LDAP
|
|
7
|
+
* @param {Object} options - The adapter configuration options.
|
|
8
|
+
* @param {String} options.url - The LDAP server URL. Must start with `ldap://` or `ldaps://`.
|
|
9
|
+
* @param {String} options.suffix - The LDAP suffix for user distinguished names (DN).
|
|
10
|
+
* @param {String} [options.dn] - The distinguished name (DN) template for user authentication. Replace `{{id}}` with the username.
|
|
11
|
+
* @param {Object} [options.tlsOptions] - Options for LDAPS TLS connections.
|
|
12
|
+
* @param {String} [options.groupCn] - The common name (CN) of the group to verify user membership.
|
|
13
|
+
* @param {String} [options.groupFilter] - The LDAP search filter for groups, with `{{id}}` replaced by the username.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} authData - The authentication data provided by the client.
|
|
16
|
+
* @param {String} authData.id - The user's LDAP username.
|
|
17
|
+
* @param {String} authData.password - The user's LDAP password.
|
|
18
|
+
*
|
|
19
|
+
* @description
|
|
20
|
+
* ## Parse Server Configuration
|
|
21
|
+
* To configure Parse Server for LDAP authentication, use the following structure:
|
|
22
|
+
* ```javascript
|
|
23
|
+
* {
|
|
24
|
+
* auth: {
|
|
25
|
+
* ldap: {
|
|
26
|
+
* url: 'ldaps://ldap.example.com',
|
|
27
|
+
* suffix: 'ou=users,dc=example,dc=com',
|
|
28
|
+
* groupCn: 'admins',
|
|
29
|
+
* groupFilter: '(memberUid={{id}})',
|
|
30
|
+
* tlsOptions: {
|
|
31
|
+
* rejectUnauthorized: false
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* ## Authentication Process
|
|
39
|
+
* 1. Validates the provided `authData` using an LDAP bind operation.
|
|
40
|
+
* 2. Optionally, verifies that the user belongs to a specific group by performing an LDAP search using the provided `groupCn` or `groupFilter`.
|
|
41
|
+
*
|
|
42
|
+
* ## Auth Payload
|
|
43
|
+
* The adapter requires the following `authData` fields:
|
|
44
|
+
* - `id`: The user's LDAP username.
|
|
45
|
+
* - `password`: The user's LDAP password.
|
|
46
|
+
*
|
|
47
|
+
* ### Example Auth Payload
|
|
48
|
+
* ```json
|
|
49
|
+
* {
|
|
50
|
+
* "ldap": {
|
|
51
|
+
* "id": "jdoe",
|
|
52
|
+
* "password": "password123"
|
|
53
|
+
* }
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @example <caption>Configuration Example</caption>
|
|
58
|
+
* // Example Parse Server configuration:
|
|
59
|
+
* const config = {
|
|
60
|
+
* auth: {
|
|
61
|
+
* ldap: {
|
|
62
|
+
* url: 'ldaps://ldap.example.com',
|
|
63
|
+
* suffix: 'ou=users,dc=example,dc=com',
|
|
64
|
+
* groupCn: 'admins',
|
|
65
|
+
* groupFilter: '(memberUid={{id}})',
|
|
66
|
+
* tlsOptions: {
|
|
67
|
+
* rejectUnauthorized: false
|
|
68
|
+
* }
|
|
69
|
+
* }
|
|
70
|
+
* }
|
|
71
|
+
* };
|
|
72
|
+
*
|
|
73
|
+
* @see {@link https://ldap.com/ LDAP Basics}
|
|
74
|
+
* @see {@link https://ldap.com/ldap-filters/ LDAP Filters}
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
const ldapjs = require('ldapjs');
|
|
78
|
+
const Parse = require('parse/node').Parse;
|
|
79
|
+
|
|
80
|
+
// Escape LDAP DN special characters per RFC 4514
|
|
81
|
+
// https://datatracker.ietf.org/doc/html/rfc4514#section-2.4
|
|
82
|
+
function escapeDN(value) {
|
|
83
|
+
let escaped = value.replace(/\\/g, '\\\\').replace(/,/g, '\\,').replace(/=/g, '\\=').replace(/\+/g, '\\+').replace(/</g, '\\<').replace(/>/g, '\\>').replace(/#/g, '\\#').replace(/;/g, '\\;').replace(/"/g, '\\"');
|
|
84
|
+
if (escaped.startsWith(' ')) {
|
|
85
|
+
escaped = '\\ ' + escaped.slice(1);
|
|
86
|
+
}
|
|
87
|
+
if (escaped.endsWith(' ')) {
|
|
88
|
+
escaped = escaped.slice(0, -1) + '\\ ';
|
|
89
|
+
}
|
|
90
|
+
return escaped;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Escape LDAP filter special characters per RFC 4515
|
|
94
|
+
// https://datatracker.ietf.org/doc/html/rfc4515#section-3
|
|
95
|
+
function escapeFilter(value) {
|
|
96
|
+
// eslint-disable-next-line no-control-regex
|
|
97
|
+
return value.replace(/[\\*()\x00]/g, ch => '\\' + ch.charCodeAt(0).toString(16).padStart(2, '0'));
|
|
98
|
+
}
|
|
99
|
+
function validateAuthData(authData, options) {
|
|
100
|
+
if (!optionsAreValid(options)) {
|
|
101
|
+
return new Promise((_, reject) => {
|
|
102
|
+
reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP auth configuration missing'));
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const clientOptions = options.url.startsWith('ldaps://') ? {
|
|
106
|
+
url: options.url,
|
|
107
|
+
tlsOptions: options.tlsOptions
|
|
108
|
+
} : {
|
|
109
|
+
url: options.url
|
|
110
|
+
};
|
|
111
|
+
if (typeof authData.id !== 'string') {
|
|
112
|
+
return Promise.reject(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAP: Wrong username or password'));
|
|
113
|
+
}
|
|
114
|
+
const client = ldapjs.createClient(clientOptions);
|
|
115
|
+
const escapedId = escapeDN(authData.id);
|
|
116
|
+
const userCn = typeof options.dn === 'string' ? options.dn.replace('{{id}}', escapedId) : `uid=${escapedId},${options.suffix}`;
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
client.bind(userCn, authData.password, ldapError => {
|
|
119
|
+
delete authData.password;
|
|
120
|
+
if (ldapError) {
|
|
121
|
+
let error;
|
|
122
|
+
switch (ldapError.code) {
|
|
123
|
+
case 49:
|
|
124
|
+
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAP: Wrong username or password');
|
|
125
|
+
break;
|
|
126
|
+
case 'DEPTH_ZERO_SELF_SIGNED_CERT':
|
|
127
|
+
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAPS: Certificate mismatch');
|
|
128
|
+
break;
|
|
129
|
+
default:
|
|
130
|
+
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAP: Somthing went wrong (' + ldapError.code + ')');
|
|
131
|
+
}
|
|
132
|
+
reject(error);
|
|
133
|
+
client.destroy(ldapError);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (typeof options.groupCn === 'string' && typeof options.groupFilter === 'string') {
|
|
137
|
+
searchForGroup(client, options, authData.id, resolve, reject);
|
|
138
|
+
} else {
|
|
139
|
+
client.unbind();
|
|
140
|
+
client.destroy();
|
|
141
|
+
resolve();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function optionsAreValid(options) {
|
|
147
|
+
return typeof options === 'object' && typeof options.suffix === 'string' && typeof options.url === 'string' && (options.url.startsWith('ldap://') || options.url.startsWith('ldaps://') && typeof options.tlsOptions === 'object');
|
|
148
|
+
}
|
|
149
|
+
function searchForGroup(client, options, id, resolve, reject) {
|
|
150
|
+
const filter = options.groupFilter.replace(/{{id}}/gi, escapeFilter(id));
|
|
151
|
+
const opts = {
|
|
152
|
+
scope: 'sub',
|
|
153
|
+
filter: filter
|
|
154
|
+
};
|
|
155
|
+
let found = false;
|
|
156
|
+
client.search(options.suffix, opts, (searchError, res) => {
|
|
157
|
+
if (searchError) {
|
|
158
|
+
client.unbind();
|
|
159
|
+
client.destroy();
|
|
160
|
+
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
|
|
161
|
+
}
|
|
162
|
+
res.on('searchEntry', entry => {
|
|
163
|
+
if (entry.pojo.attributes.find(obj => obj.type === 'cn').values.includes(options.groupCn)) {
|
|
164
|
+
found = true;
|
|
165
|
+
client.unbind();
|
|
166
|
+
client.destroy();
|
|
167
|
+
return resolve();
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
res.on('end', () => {
|
|
171
|
+
if (!found) {
|
|
172
|
+
client.unbind();
|
|
173
|
+
client.destroy();
|
|
174
|
+
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP: User not in group'));
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
res.on('error', () => {
|
|
178
|
+
client.unbind();
|
|
179
|
+
client.destroy();
|
|
180
|
+
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function validateAppId() {
|
|
185
|
+
return Promise.resolve();
|
|
186
|
+
}
|
|
187
|
+
module.exports = {
|
|
188
|
+
validateAppId,
|
|
189
|
+
validateAuthData,
|
|
190
|
+
escapeDN,
|
|
191
|
+
escapeFilter
|
|
192
|
+
};
|
|
193
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJsZGFwanMiLCJyZXF1aXJlIiwiUGFyc2UiLCJlc2NhcGVETiIsInZhbHVlIiwiZXNjYXBlZCIsInJlcGxhY2UiLCJzdGFydHNXaXRoIiwic2xpY2UiLCJlbmRzV2l0aCIsImVzY2FwZUZpbHRlciIsImNoIiwiY2hhckNvZGVBdCIsInRvU3RyaW5nIiwicGFkU3RhcnQiLCJ2YWxpZGF0ZUF1dGhEYXRhIiwiYXV0aERhdGEiLCJvcHRpb25zIiwib3B0aW9uc0FyZVZhbGlkIiwiUHJvbWlzZSIsIl8iLCJyZWplY3QiLCJFcnJvciIsIklOVEVSTkFMX1NFUlZFUl9FUlJPUiIsImNsaWVudE9wdGlvbnMiLCJ1cmwiLCJ0bHNPcHRpb25zIiwiaWQiLCJPQkpFQ1RfTk9UX0ZPVU5EIiwiY2xpZW50IiwiY3JlYXRlQ2xpZW50IiwiZXNjYXBlZElkIiwidXNlckNuIiwiZG4iLCJzdWZmaXgiLCJyZXNvbHZlIiwiYmluZCIsInBhc3N3b3JkIiwibGRhcEVycm9yIiwiZXJyb3IiLCJjb2RlIiwiZGVzdHJveSIsImdyb3VwQ24iLCJncm91cEZpbHRlciIsInNlYXJjaEZvckdyb3VwIiwidW5iaW5kIiwiZmlsdGVyIiwib3B0cyIsInNjb3BlIiwiZm91bmQiLCJzZWFyY2giLCJzZWFyY2hFcnJvciIsInJlcyIsIm9uIiwiZW50cnkiLCJwb2pvIiwiYXR0cmlidXRlcyIsImZpbmQiLCJvYmoiLCJ0eXBlIiwidmFsdWVzIiwiaW5jbHVkZXMiLCJ2YWxpZGF0ZUFwcElkIiwibW9kdWxlIiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9BZGFwdGVycy9BdXRoL2xkYXAuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBQYXJzZSBTZXJ2ZXIgYXV0aGVudGljYXRpb24gYWRhcHRlciBmb3IgTERBUC5cbiAqXG4gKiBAY2xhc3MgTERBUFxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgYWRhcHRlciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gKiBAcGFyYW0ge1N0cmluZ30gb3B0aW9ucy51cmwgLSBUaGUgTERBUCBzZXJ2ZXIgVVJMLiBNdXN0IHN0YXJ0IHdpdGggYGxkYXA6Ly9gIG9yIGBsZGFwczovL2AuXG4gKiBAcGFyYW0ge1N0cmluZ30gb3B0aW9ucy5zdWZmaXggLSBUaGUgTERBUCBzdWZmaXggZm9yIHVzZXIgZGlzdGluZ3Vpc2hlZCBuYW1lcyAoRE4pLlxuICogQHBhcmFtIHtTdHJpbmd9IFtvcHRpb25zLmRuXSAtIFRoZSBkaXN0aW5ndWlzaGVkIG5hbWUgKEROKSB0ZW1wbGF0ZSBmb3IgdXNlciBhdXRoZW50aWNhdGlvbi4gUmVwbGFjZSBge3tpZH19YCB3aXRoIHRoZSB1c2VybmFtZS5cbiAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0aW9ucy50bHNPcHRpb25zXSAtIE9wdGlvbnMgZm9yIExEQVBTIFRMUyBjb25uZWN0aW9ucy5cbiAqIEBwYXJhbSB7U3RyaW5nfSBbb3B0aW9ucy5ncm91cENuXSAtIFRoZSBjb21tb24gbmFtZSAoQ04pIG9mIHRoZSBncm91cCB0byB2ZXJpZnkgdXNlciBtZW1iZXJzaGlwLlxuICogQHBhcmFtIHtTdHJpbmd9IFtvcHRpb25zLmdyb3VwRmlsdGVyXSAtIFRoZSBMREFQIHNlYXJjaCBmaWx0ZXIgZm9yIGdyb3Vwcywgd2l0aCBge3tpZH19YCByZXBsYWNlZCBieSB0aGUgdXNlcm5hbWUuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IGF1dGhEYXRhIC0gVGhlIGF1dGhlbnRpY2F0aW9uIGRhdGEgcHJvdmlkZWQgYnkgdGhlIGNsaWVudC5cbiAqIEBwYXJhbSB7U3RyaW5nfSBhdXRoRGF0YS5pZCAtIFRoZSB1c2VyJ3MgTERBUCB1c2VybmFtZS5cbiAqIEBwYXJhbSB7U3RyaW5nfSBhdXRoRGF0YS5wYXNzd29yZCAtIFRoZSB1c2VyJ3MgTERBUCBwYXNzd29yZC5cbiAqXG4gKiBAZGVzY3JpcHRpb25cbiAqICMjIFBhcnNlIFNlcnZlciBDb25maWd1cmF0aW9uXG4gKiBUbyBjb25maWd1cmUgUGFyc2UgU2VydmVyIGZvciBMREFQIGF1dGhlbnRpY2F0aW9uLCB1c2UgdGhlIGZvbGxvd2luZyBzdHJ1Y3R1cmU6XG4gKiBgYGBqYXZhc2NyaXB0XG4gKiB7XG4gKiAgIGF1dGg6IHtcbiAqICAgICBsZGFwOiB7XG4gKiAgICAgICB1cmw6ICdsZGFwczovL2xkYXAuZXhhbXBsZS5jb20nLFxuICogICAgICAgc3VmZml4OiAnb3U9dXNlcnMsZGM9ZXhhbXBsZSxkYz1jb20nLFxuICogICAgICAgZ3JvdXBDbjogJ2FkbWlucycsXG4gKiAgICAgICBncm91cEZpbHRlcjogJyhtZW1iZXJVaWQ9e3tpZH19KScsXG4gKiAgICAgICB0bHNPcHRpb25zOiB7XG4gKiAgICAgICAgIHJlamVjdFVuYXV0aG9yaXplZDogZmFsc2VcbiAqICAgICAgIH1cbiAqICAgICB9XG4gKiAgIH1cbiAqIH1cbiAqIGBgYFxuICpcbiAqICMjIEF1dGhlbnRpY2F0aW9uIFByb2Nlc3NcbiAqIDEuIFZhbGlkYXRlcyB0aGUgcHJvdmlkZWQgYGF1dGhEYXRhYCB1c2luZyBhbiBMREFQIGJpbmQgb3BlcmF0aW9uLlxuICogMi4gT3B0aW9uYWxseSwgdmVyaWZpZXMgdGhhdCB0aGUgdXNlciBiZWxvbmdzIHRvIGEgc3BlY2lmaWMgZ3JvdXAgYnkgcGVyZm9ybWluZyBhbiBMREFQIHNlYXJjaCB1c2luZyB0aGUgcHJvdmlkZWQgYGdyb3VwQ25gIG9yIGBncm91cEZpbHRlcmAuXG4gKlxuICogIyMgQXV0aCBQYXlsb2FkXG4gKiBUaGUgYWRhcHRlciByZXF1aXJlcyB0aGUgZm9sbG93aW5nIGBhdXRoRGF0YWAgZmllbGRzOlxuICogLSBgaWRgOiBUaGUgdXNlcidzIExEQVAgdXNlcm5hbWUuXG4gKiAtIGBwYXNzd29yZGA6IFRoZSB1c2VyJ3MgTERBUCBwYXNzd29yZC5cbiAqXG4gKiAjIyMgRXhhbXBsZSBBdXRoIFBheWxvYWRcbiAqIGBgYGpzb25cbiAqIHtcbiAqICAgXCJsZGFwXCI6IHtcbiAqICAgICBcImlkXCI6IFwiamRvZVwiLFxuICogICAgIFwicGFzc3dvcmRcIjogXCJwYXNzd29yZDEyM1wiXG4gKiAgIH1cbiAqIH1cbiAqIGBgYFxuICpcbiAqIEBleGFtcGxlIDxjYXB0aW9uPkNvbmZpZ3VyYXRpb24gRXhhbXBsZTwvY2FwdGlvbj5cbiAqIC8vIEV4YW1wbGUgUGFyc2UgU2VydmVyIGNvbmZpZ3VyYXRpb246XG4gKiBjb25zdCBjb25maWcgPSB7XG4gKiAgIGF1dGg6IHtcbiAqICAgICBsZGFwOiB7XG4gKiAgICAgICB1cmw6ICdsZGFwczovL2xkYXAuZXhhbXBsZS5jb20nLFxuICogICAgICAgc3VmZml4OiAnb3U9dXNlcnMsZGM9ZXhhbXBsZSxkYz1jb20nLFxuICogICAgICAgZ3JvdXBDbjogJ2FkbWlucycsXG4gKiAgICAgICBncm91cEZpbHRlcjogJyhtZW1iZXJVaWQ9e3tpZH19KScsXG4gKiAgICAgICB0bHNPcHRpb25zOiB7XG4gKiAgICAgICAgIHJlamVjdFVuYXV0aG9yaXplZDogZmFsc2VcbiAqICAgICAgIH1cbiAqICAgICB9XG4gKiAgIH1cbiAqIH07XG4gKlxuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly9sZGFwLmNvbS8gTERBUCBCYXNpY3N9XG4gKiBAc2VlIHtAbGluayBodHRwczovL2xkYXAuY29tL2xkYXAtZmlsdGVycy8gTERBUCBGaWx0ZXJzfVxuICovXG5cblxuY29uc3QgbGRhcGpzID0gcmVxdWlyZSgnbGRhcGpzJyk7XG5jb25zdCBQYXJzZSA9IHJlcXVpcmUoJ3BhcnNlL25vZGUnKS5QYXJzZTtcblxuLy8gRXNjYXBlIExEQVAgRE4gc3BlY2lhbCBjaGFyYWN0ZXJzIHBlciBSRkMgNDUxNFxuLy8gaHR0cHM6Ly9kYXRhdHJhY2tlci5pZXRmLm9yZy9kb2MvaHRtbC9yZmM0NTE0I3NlY3Rpb24tMi40XG5mdW5jdGlvbiBlc2NhcGVETih2YWx1ZSkge1xuICBsZXQgZXNjYXBlZCA9IHZhbHVlXG4gICAgLnJlcGxhY2UoL1xcXFwvZywgJ1xcXFxcXFxcJylcbiAgICAucmVwbGFjZSgvLC9nLCAnXFxcXCwnKVxuICAgIC5yZXBsYWNlKC89L2csICdcXFxcPScpXG4gICAgLnJlcGxhY2UoL1xcKy9nLCAnXFxcXCsnKVxuICAgIC5yZXBsYWNlKC88L2csICdcXFxcPCcpXG4gICAgLnJlcGxhY2UoLz4vZywgJ1xcXFw+JylcbiAgICAucmVwbGFjZSgvIy9nLCAnXFxcXCMnKVxuICAgIC5yZXBsYWNlKC87L2csICdcXFxcOycpXG4gICAgLnJlcGxhY2UoL1wiL2csICdcXFxcXCInKTtcbiAgaWYgKGVzY2FwZWQuc3RhcnRzV2l0aCgnICcpKSB7XG4gICAgZXNjYXBlZCA9ICdcXFxcICcgKyBlc2NhcGVkLnNsaWNlKDEpO1xuICB9XG4gIGlmIChlc2NhcGVkLmVuZHNXaXRoKCcgJykpIHtcbiAgICBlc2NhcGVkID0gZXNjYXBlZC5zbGljZSgwLCAtMSkgKyAnXFxcXCAnO1xuICB9XG4gIHJldHVybiBlc2NhcGVkO1xufVxuXG4vLyBFc2NhcGUgTERBUCBmaWx0ZXIgc3BlY2lhbCBjaGFyYWN0ZXJzIHBlciBSRkMgNDUxNVxuLy8gaHR0cHM6Ly9kYXRhdHJhY2tlci5pZXRmLm9yZy9kb2MvaHRtbC9yZmM0NTE1I3NlY3Rpb24tM1xuZnVuY3Rpb24gZXNjYXBlRmlsdGVyKHZhbHVlKSB7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb250cm9sLXJlZ2V4XG4gIHJldHVybiB2YWx1ZS5yZXBsYWNlKC9bXFxcXCooKVxceDAwXS9nLCBjaCA9PlxuICAgICdcXFxcJyArIGNoLmNoYXJDb2RlQXQoMCkudG9TdHJpbmcoMTYpLnBhZFN0YXJ0KDIsICcwJylcbiAgKTtcbn1cblxuZnVuY3Rpb24gdmFsaWRhdGVBdXRoRGF0YShhdXRoRGF0YSwgb3B0aW9ucykge1xuICBpZiAoIW9wdGlvbnNBcmVWYWxpZChvcHRpb25zKSkge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgoXywgcmVqZWN0KSA9PiB7XG4gICAgICByZWplY3QobmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLklOVEVSTkFMX1NFUlZFUl9FUlJPUiwgJ0xEQVAgYXV0aCBjb25maWd1cmF0aW9uIG1pc3NpbmcnKSk7XG4gICAgfSk7XG4gIH1cbiAgY29uc3QgY2xpZW50T3B0aW9ucyA9IG9wdGlvbnMudXJsLnN0YXJ0c1dpdGgoJ2xkYXBzOi8vJylcbiAgICA/IHsgdXJsOiBvcHRpb25zLnVybCwgdGxzT3B0aW9uczogb3B0aW9ucy50bHNPcHRpb25zIH1cbiAgICA6IHsgdXJsOiBvcHRpb25zLnVybCB9O1xuXG4gIGlmICh0eXBlb2YgYXV0aERhdGEuaWQgIT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIFByb21pc2UucmVqZWN0KFxuICAgICAgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsICdMREFQOiBXcm9uZyB1c2VybmFtZSBvciBwYXNzd29yZCcpXG4gICAgKTtcbiAgfVxuICBjb25zdCBjbGllbnQgPSBsZGFwanMuY3JlYXRlQ2xpZW50KGNsaWVudE9wdGlvbnMpO1xuICBjb25zdCBlc2NhcGVkSWQgPSBlc2NhcGVETihhdXRoRGF0YS5pZCk7XG4gIGNvbnN0IHVzZXJDbiA9XG4gICAgdHlwZW9mIG9wdGlvbnMuZG4gPT09ICdzdHJpbmcnXG4gICAgICA/IG9wdGlvbnMuZG4ucmVwbGFjZSgne3tpZH19JywgZXNjYXBlZElkKVxuICAgICAgOiBgdWlkPSR7ZXNjYXBlZElkfSwke29wdGlvbnMuc3VmZml4fWA7XG5cbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBjbGllbnQuYmluZCh1c2VyQ24sIGF1dGhEYXRhLnBhc3N3b3JkLCBsZGFwRXJyb3IgPT4ge1xuICAgICAgZGVsZXRlIGF1dGhEYXRhLnBhc3N3b3JkO1xuICAgICAgaWYgKGxkYXBFcnJvcikge1xuICAgICAgICBsZXQgZXJyb3I7XG4gICAgICAgIHN3aXRjaCAobGRhcEVycm9yLmNvZGUpIHtcbiAgICAgICAgICBjYXNlIDQ5OlxuICAgICAgICAgICAgZXJyb3IgPSBuZXcgUGFyc2UuRXJyb3IoXG4gICAgICAgICAgICAgIFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsXG4gICAgICAgICAgICAgICdMREFQOiBXcm9uZyB1c2VybmFtZSBvciBwYXNzd29yZCdcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdERVBUSF9aRVJPX1NFTEZfU0lHTkVEX0NFUlQnOlxuICAgICAgICAgICAgZXJyb3IgPSBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCwgJ0xEQVBTOiBDZXJ0aWZpY2F0ZSBtaXNtYXRjaCcpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIGVycm9yID0gbmV3IFBhcnNlLkVycm9yKFxuICAgICAgICAgICAgICBQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELFxuICAgICAgICAgICAgICAnTERBUDogU29tdGhpbmcgd2VudCB3cm9uZyAoJyArIGxkYXBFcnJvci5jb2RlICsgJyknXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHJlamVjdChlcnJvcik7XG4gICAgICAgIGNsaWVudC5kZXN0cm95KGxkYXBFcnJvcik7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgaWYgKHR5cGVvZiBvcHRpb25zLmdyb3VwQ24gPT09ICdzdHJpbmcnICYmIHR5cGVvZiBvcHRpb25zLmdyb3VwRmlsdGVyID09PSAnc3RyaW5nJykge1xuICAgICAgICBzZWFyY2hGb3JHcm91cChjbGllbnQsIG9wdGlvbnMsIGF1dGhEYXRhLmlkLCByZXNvbHZlLCByZWplY3QpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY2xpZW50LnVuYmluZCgpO1xuICAgICAgICBjbGllbnQuZGVzdHJveSgpO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH0pO1xufVxuXG5mdW5jdGlvbiBvcHRpb25zQXJlVmFsaWQob3B0aW9ucykge1xuICByZXR1cm4gKFxuICAgIHR5cGVvZiBvcHRpb25zID09PSAnb2JqZWN0JyAmJlxuICAgIHR5cGVvZiBvcHRpb25zLnN1ZmZpeCA9PT0gJ3N0cmluZycgJiZcbiAgICB0eXBlb2Ygb3B0aW9ucy51cmwgPT09ICdzdHJpbmcnICYmXG4gICAgKG9wdGlvbnMudXJsLnN0YXJ0c1dpdGgoJ2xkYXA6Ly8nKSB8fFxuICAgICAgKG9wdGlvbnMudXJsLnN0YXJ0c1dpdGgoJ2xkYXBzOi8vJykgJiYgdHlwZW9mIG9wdGlvbnMudGxzT3B0aW9ucyA9PT0gJ29iamVjdCcpKVxuICApO1xufVxuXG5mdW5jdGlvbiBzZWFyY2hGb3JHcm91cChjbGllbnQsIG9wdGlvbnMsIGlkLCByZXNvbHZlLCByZWplY3QpIHtcbiAgY29uc3QgZmlsdGVyID0gb3B0aW9ucy5ncm91cEZpbHRlci5yZXBsYWNlKC97e2lkfX0vZ2ksIGVzY2FwZUZpbHRlcihpZCkpO1xuICBjb25zdCBvcHRzID0ge1xuICAgIHNjb3BlOiAnc3ViJyxcbiAgICBmaWx0ZXI6IGZpbHRlcixcbiAgfTtcbiAgbGV0IGZvdW5kID0gZmFsc2U7XG4gIGNsaWVudC5zZWFyY2gob3B0aW9ucy5zdWZmaXgsIG9wdHMsIChzZWFyY2hFcnJvciwgcmVzKSA9PiB7XG4gICAgaWYgKHNlYXJjaEVycm9yKSB7XG4gICAgICBjbGllbnQudW5iaW5kKCk7XG4gICAgICBjbGllbnQuZGVzdHJveSgpO1xuICAgICAgcmV0dXJuIHJlamVjdChuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuSU5URVJOQUxfU0VSVkVSX0VSUk9SLCAnTERBUCBncm91cCBzZWFyY2ggZmFpbGVkJykpO1xuICAgIH1cbiAgICByZXMub24oJ3NlYXJjaEVudHJ5JywgZW50cnkgPT4ge1xuICAgICAgaWYgKGVudHJ5LnBvam8uYXR0cmlidXRlcy5maW5kKG9iaiA9PiBvYmoudHlwZSA9PT0gJ2NuJykudmFsdWVzLmluY2x1ZGVzKG9wdGlvbnMuZ3JvdXBDbikpIHtcbiAgICAgICAgZm91bmQgPSB0cnVlO1xuICAgICAgICBjbGllbnQudW5iaW5kKCk7XG4gICAgICAgIGNsaWVudC5kZXN0cm95KCk7XG4gICAgICAgIHJldHVybiByZXNvbHZlKCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgcmVzLm9uKCdlbmQnLCAoKSA9PiB7XG4gICAgICBpZiAoIWZvdW5kKSB7XG4gICAgICAgIGNsaWVudC51bmJpbmQoKTtcbiAgICAgICAgY2xpZW50LmRlc3Ryb3koKTtcbiAgICAgICAgcmV0dXJuIHJlamVjdChcbiAgICAgICAgICBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuSU5URVJOQUxfU0VSVkVSX0VSUk9SLCAnTERBUDogVXNlciBub3QgaW4gZ3JvdXAnKVxuICAgICAgICApO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHJlcy5vbignZXJyb3InLCAoKSA9PiB7XG4gICAgICBjbGllbnQudW5iaW5kKCk7XG4gICAgICBjbGllbnQuZGVzdHJveSgpO1xuICAgICAgcmV0dXJuIHJlamVjdChuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuSU5URVJOQUxfU0VSVkVSX0VSUk9SLCAnTERBUCBncm91cCBzZWFyY2ggZmFpbGVkJykpO1xuICAgIH0pO1xuICB9KTtcbn1cblxuZnVuY3Rpb24gdmFsaWRhdGVBcHBJZCgpIHtcbiAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgdmFsaWRhdGVBcHBJZCxcbiAgdmFsaWRhdGVBdXRoRGF0YSxcbiAgZXNjYXBlRE4sXG4gIGVzY2FwZUZpbHRlcixcbn07XG4iXSwibWFwcGluZ3MiOiI7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7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
|