parse-server 2.8.4 → 8.6.2
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 +167 -25
- package/NOTICE +10 -0
- package/README.md +929 -278
- package/lib/AccountLockout.js +47 -30
- package/lib/Adapters/AdapterLoader.js +21 -6
- package/lib/Adapters/Analytics/AnalyticsAdapter.js +15 -12
- package/lib/Adapters/Auth/AuthAdapter.js +116 -13
- package/lib/Adapters/Auth/BaseCodeAuthAdapter.js +99 -0
- package/lib/Adapters/Auth/OAuth1Client.js +27 -46
- package/lib/Adapters/Auth/apple.js +123 -0
- package/lib/Adapters/Auth/facebook.js +162 -35
- package/lib/Adapters/Auth/gcenter.js +217 -0
- package/lib/Adapters/Auth/github.js +118 -48
- package/lib/Adapters/Auth/google.js +160 -51
- package/lib/Adapters/Auth/gpgames.js +125 -0
- package/lib/Adapters/Auth/httpsRequest.js +6 -7
- package/lib/Adapters/Auth/index.js +170 -62
- package/lib/Adapters/Auth/instagram.js +114 -40
- package/lib/Adapters/Auth/janraincapture.js +52 -23
- package/lib/Adapters/Auth/janrainengage.js +19 -36
- package/lib/Adapters/Auth/keycloak.js +148 -0
- package/lib/Adapters/Auth/ldap.js +167 -0
- package/lib/Adapters/Auth/line.js +125 -0
- package/lib/Adapters/Auth/linkedin.js +111 -55
- package/lib/Adapters/Auth/meetup.js +24 -34
- package/lib/Adapters/Auth/mfa.js +324 -0
- package/lib/Adapters/Auth/microsoft.js +111 -0
- package/lib/Adapters/Auth/oauth2.js +97 -162
- package/lib/Adapters/Auth/phantauth.js +53 -0
- package/lib/Adapters/Auth/qq.js +108 -49
- package/lib/Adapters/Auth/spotify.js +107 -55
- package/lib/Adapters/Auth/twitter.js +188 -48
- package/lib/Adapters/Auth/utils.js +28 -0
- package/lib/Adapters/Auth/vkontakte.js +26 -39
- package/lib/Adapters/Auth/wechat.js +106 -44
- package/lib/Adapters/Auth/weibo.js +132 -58
- package/lib/Adapters/Cache/CacheAdapter.js +13 -8
- package/lib/Adapters/Cache/InMemoryCache.js +3 -13
- package/lib/Adapters/Cache/InMemoryCacheAdapter.js +5 -13
- package/lib/Adapters/Cache/LRUCache.js +13 -27
- package/lib/Adapters/Cache/NullCacheAdapter.js +3 -8
- package/lib/Adapters/Cache/RedisCacheAdapter.js +85 -76
- package/lib/Adapters/Cache/SchemaCache.js +25 -0
- package/lib/Adapters/Email/MailAdapter.js +10 -8
- package/lib/Adapters/Files/FilesAdapter.js +83 -25
- package/lib/Adapters/Files/GridFSBucketAdapter.js +231 -0
- package/lib/Adapters/Files/GridStoreAdapter.js +4 -91
- package/lib/Adapters/Logger/LoggerAdapter.js +18 -14
- package/lib/Adapters/Logger/WinstonLogger.js +69 -88
- package/lib/Adapters/Logger/WinstonLoggerAdapter.js +7 -16
- package/lib/Adapters/MessageQueue/EventEmitterMQ.js +8 -26
- package/lib/Adapters/PubSub/EventEmitterPubSub.js +12 -25
- package/lib/Adapters/PubSub/PubSubAdapter.js +34 -0
- package/lib/Adapters/PubSub/RedisPubSub.js +42 -19
- package/lib/Adapters/Push/PushAdapter.js +14 -7
- package/lib/Adapters/Storage/Mongo/MongoCollection.js +137 -45
- package/lib/Adapters/Storage/Mongo/MongoSchemaCollection.js +158 -63
- package/lib/Adapters/Storage/Mongo/MongoStorageAdapter.js +320 -168
- package/lib/Adapters/Storage/Mongo/MongoTransform.js +279 -306
- package/lib/Adapters/Storage/Postgres/PostgresClient.js +14 -10
- package/lib/Adapters/Storage/Postgres/PostgresConfigParser.js +47 -21
- package/lib/Adapters/Storage/Postgres/PostgresStorageAdapter.js +854 -468
- package/lib/Adapters/Storage/Postgres/sql/index.js +4 -6
- package/lib/Adapters/Storage/StorageAdapter.js +1 -1
- package/lib/Adapters/WebSocketServer/WSAdapter.js +35 -0
- package/lib/Adapters/WebSocketServer/WSSAdapter.js +66 -0
- package/lib/Auth.js +488 -125
- package/lib/ClientSDK.js +2 -6
- package/lib/Config.js +525 -94
- package/lib/Controllers/AdaptableController.js +5 -25
- package/lib/Controllers/AnalyticsController.js +22 -23
- package/lib/Controllers/CacheController.js +10 -31
- package/lib/Controllers/DatabaseController.js +767 -313
- package/lib/Controllers/FilesController.js +49 -54
- package/lib/Controllers/HooksController.js +80 -84
- package/lib/Controllers/LiveQueryController.js +35 -22
- package/lib/Controllers/LoggerController.js +22 -58
- package/lib/Controllers/ParseGraphQLController.js +293 -0
- package/lib/Controllers/PushController.js +58 -49
- package/lib/Controllers/SchemaController.js +916 -422
- package/lib/Controllers/UserController.js +265 -180
- package/lib/Controllers/index.js +90 -125
- package/lib/Controllers/types.js +1 -1
- package/lib/Deprecator/Deprecations.js +30 -0
- package/lib/Deprecator/Deprecator.js +127 -0
- package/lib/Error.js +48 -0
- package/lib/GraphQL/ParseGraphQLSchema.js +375 -0
- package/lib/GraphQL/ParseGraphQLServer.js +214 -0
- package/lib/GraphQL/helpers/objectsMutations.js +30 -0
- package/lib/GraphQL/helpers/objectsQueries.js +246 -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 +1098 -0
- package/lib/GraphQL/loaders/defaultRelaySchema.js +53 -0
- package/lib/GraphQL/loaders/filesMutations.js +107 -0
- package/lib/GraphQL/loaders/functionsMutations.js +78 -0
- package/lib/GraphQL/loaders/parseClassMutations.js +268 -0
- package/lib/GraphQL/loaders/parseClassQueries.js +127 -0
- package/lib/GraphQL/loaders/parseClassTypes.js +493 -0
- package/lib/GraphQL/loaders/schemaDirectives.js +62 -0
- package/lib/GraphQL/loaders/schemaMutations.js +162 -0
- package/lib/GraphQL/loaders/schemaQueries.js +81 -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 +63 -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 +274 -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/KeyPromiseQueue.js +48 -0
- package/lib/LiveQuery/Client.js +25 -33
- package/lib/LiveQuery/Id.js +2 -5
- package/lib/LiveQuery/ParseCloudCodePublisher.js +26 -23
- package/lib/LiveQuery/ParseLiveQueryServer.js +560 -285
- package/lib/LiveQuery/ParsePubSub.js +7 -16
- package/lib/LiveQuery/ParseWebSocketServer.js +42 -39
- package/lib/LiveQuery/QueryTools.js +76 -15
- package/lib/LiveQuery/RequestSchema.js +111 -97
- package/lib/LiveQuery/SessionTokenCache.js +23 -36
- package/lib/LiveQuery/Subscription.js +8 -17
- package/lib/LiveQuery/equalObjects.js +2 -3
- package/lib/Options/Definitions.js +1355 -382
- package/lib/Options/docs.js +301 -62
- package/lib/Options/index.js +11 -1
- package/lib/Options/parsers.js +14 -10
- package/lib/Page.js +44 -0
- package/lib/ParseMessageQueue.js +6 -13
- package/lib/ParseServer.js +474 -235
- package/lib/ParseServerRESTController.js +102 -40
- package/lib/PromiseRouter.js +39 -50
- package/lib/Push/PushQueue.js +24 -30
- package/lib/Push/PushWorker.js +32 -56
- package/lib/Push/utils.js +22 -35
- package/lib/RestQuery.js +361 -139
- package/lib/RestWrite.js +713 -344
- package/lib/Routers/AggregateRouter.js +97 -71
- package/lib/Routers/AnalyticsRouter.js +8 -14
- package/lib/Routers/AudiencesRouter.js +16 -35
- package/lib/Routers/ClassesRouter.js +86 -72
- package/lib/Routers/CloudCodeRouter.js +28 -37
- package/lib/Routers/FeaturesRouter.js +22 -25
- package/lib/Routers/FilesRouter.js +266 -171
- package/lib/Routers/FunctionsRouter.js +87 -103
- package/lib/Routers/GlobalConfigRouter.js +94 -33
- package/lib/Routers/GraphQLRouter.js +41 -0
- package/lib/Routers/HooksRouter.js +43 -47
- package/lib/Routers/IAPValidationRouter.js +57 -70
- package/lib/Routers/InstallationsRouter.js +17 -25
- package/lib/Routers/LogsRouter.js +10 -25
- package/lib/Routers/PagesRouter.js +647 -0
- package/lib/Routers/PublicAPIRouter.js +104 -112
- package/lib/Routers/PurgeRouter.js +19 -29
- package/lib/Routers/PushRouter.js +14 -28
- package/lib/Routers/RolesRouter.js +7 -14
- package/lib/Routers/SchemasRouter.js +63 -42
- package/lib/Routers/SecurityRouter.js +34 -0
- package/lib/Routers/SessionsRouter.js +25 -38
- package/lib/Routers/UsersRouter.js +463 -190
- 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 +96 -0
- package/lib/Security/CheckGroups/CheckGroups.js +21 -0
- package/lib/Security/CheckRunner.js +213 -0
- package/lib/SharedRest.js +29 -0
- package/lib/StatusHandler.js +96 -93
- package/lib/TestUtils.js +70 -14
- package/lib/Utils.js +468 -0
- package/lib/batch.js +74 -40
- package/lib/cache.js +8 -8
- package/lib/cli/definitions/parse-live-query-server.js +4 -3
- package/lib/cli/definitions/parse-server.js +4 -3
- package/lib/cli/parse-live-query-server.js +9 -17
- package/lib/cli/parse-server.js +49 -47
- package/lib/cli/utils/commander.js +20 -29
- package/lib/cli/utils/runner.js +31 -32
- package/lib/cloud-code/Parse.Cloud.js +711 -36
- package/lib/cloud-code/Parse.Server.js +21 -0
- package/lib/cryptoUtils.js +6 -11
- package/lib/defaults.js +21 -15
- package/lib/deprecated.js +1 -1
- package/lib/index.js +78 -67
- package/lib/logger.js +12 -20
- package/lib/middlewares.js +484 -160
- package/lib/password.js +10 -6
- package/lib/request.js +175 -0
- package/lib/requiredParameter.js +4 -3
- package/lib/rest.js +157 -82
- package/lib/triggers.js +627 -185
- package/lib/vendor/README.md +3 -3
- package/lib/vendor/mongodbUrl.js +224 -137
- package/package.json +135 -57
- package/postinstall.js +38 -50
- package/public_html/invalid_verification_link.html +3 -3
- 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 +301 -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/CHANGELOG.md +0 -1246
- package/PATENTS +0 -37
- package/bin/dev +0 -37
- package/lib/.DS_Store +0 -0
- package/lib/Adapters/Auth/common.js +0 -2
- package/lib/Adapters/Auth/facebookaccountkit.js +0 -69
- package/lib/Controllers/SchemaCache.js +0 -97
- package/lib/LiveQuery/.DS_Store +0 -0
- package/lib/cli/utils/parsers.js +0 -77
- package/lib/cloud-code/.DS_Store +0 -0
- package/lib/cloud-code/HTTPResponse.js +0 -57
- package/lib/cloud-code/Untitled-1 +0 -123
- package/lib/cloud-code/httpRequest.js +0 -102
- package/lib/cloud-code/team.html +0 -123
- package/lib/graphql/ParseClass.js +0 -234
- package/lib/graphql/Schema.js +0 -197
- package/lib/graphql/index.js +0 -1
- package/lib/graphql/types/ACL.js +0 -35
- package/lib/graphql/types/Date.js +0 -25
- package/lib/graphql/types/File.js +0 -24
- package/lib/graphql/types/GeoPoint.js +0 -35
- package/lib/graphql/types/JSONObject.js +0 -30
- package/lib/graphql/types/NumberInput.js +0 -43
- package/lib/graphql/types/NumberQuery.js +0 -42
- package/lib/graphql/types/Pointer.js +0 -35
- package/lib/graphql/types/QueryConstraint.js +0 -61
- package/lib/graphql/types/StringQuery.js +0 -39
- package/lib/graphql/types/index.js +0 -110
|
@@ -1,9 +1,53 @@
|
|
|
1
|
-
|
|
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
|
+
*/
|
|
2
46
|
|
|
3
47
|
// Helper functions for accessing the Janrain Capture API.
|
|
4
|
-
var https = require('https');
|
|
5
48
|
var Parse = require('parse/node').Parse;
|
|
6
49
|
var querystring = require('querystring');
|
|
50
|
+
const httpsRequest = require('./httpsRequest');
|
|
7
51
|
|
|
8
52
|
// Returns a promise that fulfills iff this user id is valid.
|
|
9
53
|
function validateAuthData(authData, options) {
|
|
@@ -25,32 +69,17 @@ function validateAppId() {
|
|
|
25
69
|
|
|
26
70
|
// A promisey wrapper for api requests
|
|
27
71
|
function request(host, access_token) {
|
|
28
|
-
|
|
29
72
|
var query_string_data = querystring.stringify({
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
32
75
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
host: host,
|
|
37
|
-
path: '/entity?' + query_string_data
|
|
38
|
-
}, function (res) {
|
|
39
|
-
var data = '';
|
|
40
|
-
res.on('data', function (chunk) {
|
|
41
|
-
data += chunk;
|
|
42
|
-
});
|
|
43
|
-
res.on('end', function () {
|
|
44
|
-
resolve(JSON.parse(data));
|
|
45
|
-
});
|
|
46
|
-
}).on('error', function () {
|
|
47
|
-
reject('Failed to validate this access token with Janrain capture.');
|
|
48
|
-
});
|
|
76
|
+
return httpsRequest.get({
|
|
77
|
+
host: host,
|
|
78
|
+
path: '/entity?' + query_string_data
|
|
49
79
|
});
|
|
50
80
|
}
|
|
51
|
-
|
|
52
81
|
module.exports = {
|
|
53
82
|
validateAppId: validateAppId,
|
|
54
83
|
validateAuthData: validateAuthData
|
|
55
84
|
};
|
|
56
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,
|
|
85
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJQYXJzZSIsInJlcXVpcmUiLCJxdWVyeXN0cmluZyIsImh0dHBzUmVxdWVzdCIsInZhbGlkYXRlQXV0aERhdGEiLCJhdXRoRGF0YSIsIm9wdGlvbnMiLCJyZXF1ZXN0IiwiamFucmFpbl9jYXB0dXJlX2hvc3QiLCJhY2Nlc3NfdG9rZW4iLCJ0aGVuIiwiZGF0YSIsInN0YXQiLCJyZXN1bHQiLCJpZCIsIkVycm9yIiwiT0JKRUNUX05PVF9GT1VORCIsInZhbGlkYXRlQXBwSWQiLCJQcm9taXNlIiwicmVzb2x2ZSIsImhvc3QiLCJxdWVyeV9zdHJpbmdfZGF0YSIsInN0cmluZ2lmeSIsImF0dHJpYnV0ZV9uYW1lIiwiZ2V0IiwicGF0aCIsIm1vZHVsZSIsImV4cG9ydHMiXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvQWRhcHRlcnMvQXV0aC9qYW5yYWluY2FwdHVyZS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBhcnNlIFNlcnZlciBhdXRoZW50aWNhdGlvbiBhZGFwdGVyIGZvciBKYW5yYWluIENhcHR1cmUgQVBJLlxuICpcbiAqIEBjbGFzcyBKYW5yYWluQ2FwdHVyZVxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgYWRhcHRlciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gKiBAcGFyYW0ge1N0cmluZ30gb3B0aW9ucy5qYW5yYWluX2NhcHR1cmVfaG9zdCAtIFRoZSBKYW5yYWluIENhcHR1cmUgQVBJIGhvc3QuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IGF1dGhEYXRhIC0gVGhlIGF1dGhlbnRpY2F0aW9uIGRhdGEgcHJvdmlkZWQgYnkgdGhlIGNsaWVudC5cbiAqIEBwYXJhbSB7U3RyaW5nfSBhdXRoRGF0YS5pZCAtIFRoZSBKYW5yYWluIENhcHR1cmUgdXNlciBJRC5cbiAqIEBwYXJhbSB7U3RyaW5nfSBhdXRoRGF0YS5hY2Nlc3NfdG9rZW4gLSBUaGUgSmFucmFpbiBDYXB0dXJlIGFjY2VzcyB0b2tlbi5cbiAqXG4gKiBAZGVzY3JpcHRpb25cbiAqICMjIFBhcnNlIFNlcnZlciBDb25maWd1cmF0aW9uXG4gKiBUbyBjb25maWd1cmUgUGFyc2UgU2VydmVyIGZvciBKYW5yYWluIENhcHR1cmUgYXV0aGVudGljYXRpb24sIHVzZSB0aGUgZm9sbG93aW5nIHN0cnVjdHVyZTpcbiAqIGBgYGpzb25cbiAqIHtcbiAqICAgXCJhdXRoXCI6IHtcbiAqICAgICBcImphbnJhaW5cIjoge1xuICogICAgICAgXCJqYW5yYWluX2NhcHR1cmVfaG9zdFwiOiBcInlvdXItamFucmFpbi1jYXB0dXJlLWhvc3RcIlxuICogICAgIH1cbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogVGhlIGFkYXB0ZXIgcmVxdWlyZXMgdGhlIGZvbGxvd2luZyBgYXV0aERhdGFgIGZpZWxkczpcbiAqIC0gYGlkYDogVGhlIEphbnJhaW4gQ2FwdHVyZSB1c2VyIElELlxuICogLSBgYWNjZXNzX3Rva2VuYDogQW4gYXV0aG9yaXplZCBKYW5yYWluIENhcHR1cmUgYWNjZXNzIHRva2VuIGZvciB0aGUgdXNlci5cbiAqXG4gKiAjIyBBdXRoIFBheWxvYWQgRXhhbXBsZVxuICogYGBganNvblxuICoge1xuICogICBcImphbnJhaW5cIjoge1xuICogICAgIFwiaWRcIjogXCJ1c2VyJ3MgSmFucmFpbiBDYXB0dXJlIElEIGFzIGEgc3RyaW5nXCIsXG4gKiAgICAgXCJhY2Nlc3NfdG9rZW5cIjogXCJhbiBhdXRob3JpemVkIEphbnJhaW4gQ2FwdHVyZSBhY2Nlc3MgdG9rZW4gZm9yIHRoZSB1c2VyXCJcbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogIyMgTm90ZXNcbiAqIFBhcnNlIFNlcnZlciB2YWxpZGF0ZXMgdGhlIHByb3ZpZGVkIGBhdXRoRGF0YWAgdXNpbmcgdGhlIEphbnJhaW4gQ2FwdHVyZSBBUEkuXG4gKlxuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly9kb2NzLmphbnJhaW4uY29tL2FwaS9yZWdpc3RyYXRpb24vZW50aXR5LyNlbnRpdHkgSmFucmFpbiBDYXB0dXJlIEFQSSBEb2N1bWVudGF0aW9ufVxuICovXG5cblxuLy8gSGVscGVyIGZ1bmN0aW9ucyBmb3IgYWNjZXNzaW5nIHRoZSBKYW5yYWluIENhcHR1cmUgQVBJLlxudmFyIFBhcnNlID0gcmVxdWlyZSgncGFyc2Uvbm9kZScpLlBhcnNlO1xudmFyIHF1ZXJ5c3RyaW5nID0gcmVxdWlyZSgncXVlcnlzdHJpbmcnKTtcbmNvbnN0IGh0dHBzUmVxdWVzdCA9IHJlcXVpcmUoJy4vaHR0cHNSZXF1ZXN0Jyk7XG5cbi8vIFJldHVybnMgYSBwcm9taXNlIHRoYXQgZnVsZmlsbHMgaWZmIHRoaXMgdXNlciBpZCBpcyB2YWxpZC5cbmZ1bmN0aW9uIHZhbGlkYXRlQXV0aERhdGEoYXV0aERhdGEsIG9wdGlvbnMpIHtcbiAgcmV0dXJuIHJlcXVlc3Qob3B0aW9ucy5qYW5yYWluX2NhcHR1cmVfaG9zdCwgYXV0aERhdGEuYWNjZXNzX3Rva2VuKS50aGVuKGRhdGEgPT4ge1xuICAgIC8vc3VjY2Vzc2Z1bCByZXNwb25zZSB3aWxsIGhhdmUgYSBcInN0YXRcIiAoc3RhdHVzKSBvZiAnb2snIGFuZCBhIHJlc3VsdCBub2RlIHRoYXQgc3RvcmVzIHRoZSB1dWlkLCBiZWNhdXNlIHRoYXQncyBhbGwgd2UgYXNrZWQgZm9yXG4gICAgLy9zZWU6IGh0dHBzOi8vZG9jcy5qYW5yYWluLmNvbS9hcGkvcmVnaXN0cmF0aW9uL2VudGl0eS8jZW50aXR5XG4gICAgaWYgKGRhdGEgJiYgZGF0YS5zdGF0ID09ICdvaycgJiYgZGF0YS5yZXN1bHQgPT0gYXV0aERhdGEuaWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFxuICAgICAgUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCxcbiAgICAgICdKYW5yYWluIGNhcHR1cmUgYXV0aCBpcyBpbnZhbGlkIGZvciB0aGlzIHVzZXIuJ1xuICAgICk7XG4gIH0pO1xufVxuXG4vLyBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IGZ1bGZpbGxzIGlmZiB0aGlzIGFwcCBpZCBpcyB2YWxpZC5cbmZ1bmN0aW9uIHZhbGlkYXRlQXBwSWQoKSB7XG4gIC8vbm8tb3BcbiAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xufVxuXG4vLyBBIHByb21pc2V5IHdyYXBwZXIgZm9yIGFwaSByZXF1ZXN0c1xuZnVuY3Rpb24gcmVxdWVzdChob3N0LCBhY2Nlc3NfdG9rZW4pIHtcbiAgdmFyIHF1ZXJ5X3N0cmluZ19kYXRhID0gcXVlcnlzdHJpbmcuc3RyaW5naWZ5KHtcbiAgICBhY2Nlc3NfdG9rZW46IGFjY2Vzc190b2tlbixcbiAgICBhdHRyaWJ1dGVfbmFtZTogJ3V1aWQnLCAvLyB3ZSBvbmx5IG5lZWQgdG8gcHVsbCB0aGUgdXVpZCBmb3IgdGhpcyBhY2Nlc3MgdG9rZW4gdG8gbWFrZSBzdXJlIGl0IG1hdGNoZXNcbiAgfSk7XG5cbiAgcmV0dXJuIGh0dHBzUmVxdWVzdC5nZXQoeyBob3N0OiBob3N0LCBwYXRoOiAnL2VudGl0eT8nICsgcXVlcnlfc3RyaW5nX2RhdGEgfSk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICB2YWxpZGF0ZUFwcElkOiB2YWxpZGF0ZUFwcElkLFxuICB2YWxpZGF0ZUF1dGhEYXRhOiB2YWxpZGF0ZUF1dGhEYXRhLFxufTtcbiJdLCJtYXBwaW5ncyI6Ijs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFHQTtBQUNBLElBQUlBLEtBQUssR0FBR0MsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDRCxLQUFLO0FBQ3ZDLElBQUlFLFdBQVcsR0FBR0QsT0FBTyxDQUFDLGFBQWEsQ0FBQztBQUN4QyxNQUFNRSxZQUFZLEdBQUdGLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQzs7QUFFOUM7QUFDQSxTQUFTRyxnQkFBZ0JBLENBQUNDLFFBQVEsRUFBRUMsT0FBTyxFQUFFO0VBQzNDLE9BQU9DLE9BQU8sQ0FBQ0QsT0FBTyxDQUFDRSxvQkFBb0IsRUFBRUgsUUFBUSxDQUFDSSxZQUFZLENBQUMsQ0FBQ0MsSUFBSSxDQUFDQyxJQUFJLElBQUk7SUFDL0U7SUFDQTtJQUNBLElBQUlBLElBQUksSUFBSUEsSUFBSSxDQUFDQyxJQUFJLElBQUksSUFBSSxJQUFJRCxJQUFJLENBQUNFLE1BQU0sSUFBSVIsUUFBUSxDQUFDUyxFQUFFLEVBQUU7TUFDM0Q7SUFDRjtJQUNBLE1BQU0sSUFBSWQsS0FBSyxDQUFDZSxLQUFLLENBQ25CZixLQUFLLENBQUNlLEtBQUssQ0FBQ0MsZ0JBQWdCLEVBQzVCLGdEQUNGLENBQUM7RUFDSCxDQUFDLENBQUM7QUFDSjs7QUFFQTtBQUNBLFNBQVNDLGFBQWFBLENBQUEsRUFBRztFQUN2QjtFQUNBLE9BQU9DLE9BQU8sQ0FBQ0MsT0FBTyxDQUFDLENBQUM7QUFDMUI7O0FBRUE7QUFDQSxTQUFTWixPQUFPQSxDQUFDYSxJQUFJLEVBQUVYLFlBQVksRUFBRTtFQUNuQyxJQUFJWSxpQkFBaUIsR0FBR25CLFdBQVcsQ0FBQ29CLFNBQVMsQ0FBQztJQUM1Q2IsWUFBWSxFQUFFQSxZQUFZO0lBQzFCYyxjQUFjLEVBQUUsTUFBTSxDQUFFO0VBQzFCLENBQUMsQ0FBQztFQUVGLE9BQU9wQixZQUFZLENBQUNxQixHQUFHLENBQUM7SUFBRUosSUFBSSxFQUFFQSxJQUFJO0lBQUVLLElBQUksRUFBRSxVQUFVLEdBQUdKO0VBQWtCLENBQUMsQ0FBQztBQUMvRTtBQUVBSyxNQUFNLENBQUNDLE9BQU8sR0FBRztFQUNmVixhQUFhLEVBQUVBLGFBQWE7RUFDNUJiLGdCQUFnQixFQUFFQTtBQUNwQixDQUFDIiwiaWdub3JlTGlzdCI6W119
|
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
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 }; }
|
|
3
6
|
// Helper functions for accessing the Janrain Engage API.
|
|
4
|
-
var
|
|
7
|
+
var httpsRequest = require('./httpsRequest');
|
|
5
8
|
var Parse = require('parse/node').Parse;
|
|
6
9
|
var querystring = require('querystring');
|
|
7
|
-
|
|
8
10
|
// Returns a promise that fulfills iff this user id is valid.
|
|
9
11
|
function validateAuthData(authData, options) {
|
|
10
|
-
|
|
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 => {
|
|
11
20
|
//successful response will have a "stat" (status) of 'ok' and a profile node with an identifier
|
|
12
21
|
//see: http://developers.janrain.com/overview/social-login/identity-providers/user-profile-data/#normalized-user-profile-data
|
|
13
22
|
if (data && data.stat == 'ok' && data.profile.identifier == authData.id) {
|
|
@@ -24,14 +33,12 @@ function validateAppId() {
|
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
// A promisey wrapper for api requests
|
|
27
|
-
function
|
|
28
|
-
|
|
36
|
+
function apiRequest(api_key, auth_token) {
|
|
29
37
|
var post_data = querystring.stringify({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
token: auth_token,
|
|
39
|
+
apiKey: api_key,
|
|
40
|
+
format: 'json'
|
|
33
41
|
});
|
|
34
|
-
|
|
35
42
|
var post_options = {
|
|
36
43
|
host: 'rpxnow.com',
|
|
37
44
|
path: '/api/v2/auth_info',
|
|
@@ -41,34 +48,10 @@ function request(api_key, auth_token) {
|
|
|
41
48
|
'Content-Length': post_data.length
|
|
42
49
|
}
|
|
43
50
|
};
|
|
44
|
-
|
|
45
|
-
return new Promise(function (resolve, reject) {
|
|
46
|
-
// Create the post request.
|
|
47
|
-
var post_req = https.request(post_options, function (res) {
|
|
48
|
-
var data = '';
|
|
49
|
-
res.setEncoding('utf8');
|
|
50
|
-
// Append data as we receive it from the Janrain engage server.
|
|
51
|
-
res.on('data', function (d) {
|
|
52
|
-
data += d;
|
|
53
|
-
});
|
|
54
|
-
// Once we have all the data, we can parse it and return the data we want.
|
|
55
|
-
res.on('end', function () {
|
|
56
|
-
try {
|
|
57
|
-
data = JSON.parse(data);
|
|
58
|
-
} catch (e) {
|
|
59
|
-
return reject(e);
|
|
60
|
-
}
|
|
61
|
-
resolve(data);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
post_req.write(post_data);
|
|
66
|
-
post_req.end();
|
|
67
|
-
});
|
|
51
|
+
return httpsRequest.request(post_options, post_data);
|
|
68
52
|
}
|
|
69
|
-
|
|
70
53
|
module.exports = {
|
|
71
54
|
validateAppId: validateAppId,
|
|
72
55
|
validateAuthData: validateAuthData
|
|
73
56
|
};
|
|
74
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,
|
|
57
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfQ29uZmlnIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfRGVwcmVjYXRvciIsImUiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsImh0dHBzUmVxdWVzdCIsIlBhcnNlIiwicXVlcnlzdHJpbmciLCJ2YWxpZGF0ZUF1dGhEYXRhIiwiYXV0aERhdGEiLCJvcHRpb25zIiwiY29uZmlnIiwiQ29uZmlnIiwiZ2V0IiwiYXBwbGljYXRpb25JZCIsIkRlcHJlY2F0b3IiLCJsb2dSdW50aW1lRGVwcmVjYXRpb24iLCJ1c2FnZSIsImF1dGgiLCJqYW5yYWluZW5nYWdlIiwiZW5hYmxlSW5zZWN1cmVBdXRoIiwiZW5hYmxlSW5zZWN1cmVBdXRoQWRhcHRlcnMiLCJFcnJvciIsIklOVEVSTkFMX1NFUlZFUl9FUlJPUiIsImFwaVJlcXVlc3QiLCJhcGlfa2V5IiwiYXV0aF90b2tlbiIsInRoZW4iLCJkYXRhIiwic3RhdCIsInByb2ZpbGUiLCJpZGVudGlmaWVyIiwiaWQiLCJPQkpFQ1RfTk9UX0ZPVU5EIiwidmFsaWRhdGVBcHBJZCIsIlByb21pc2UiLCJyZXNvbHZlIiwicG9zdF9kYXRhIiwic3RyaW5naWZ5IiwidG9rZW4iLCJhcGlLZXkiLCJmb3JtYXQiLCJwb3N0X29wdGlvbnMiLCJob3N0IiwicGF0aCIsIm1ldGhvZCIsImhlYWRlcnMiLCJsZW5ndGgiLCJyZXF1ZXN0IiwibW9kdWxlIiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9BZGFwdGVycy9BdXRoL2phbnJhaW5lbmdhZ2UuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gSGVscGVyIGZ1bmN0aW9ucyBmb3IgYWNjZXNzaW5nIHRoZSBKYW5yYWluIEVuZ2FnZSBBUEkuXG52YXIgaHR0cHNSZXF1ZXN0ID0gcmVxdWlyZSgnLi9odHRwc1JlcXVlc3QnKTtcbnZhciBQYXJzZSA9IHJlcXVpcmUoJ3BhcnNlL25vZGUnKS5QYXJzZTtcbnZhciBxdWVyeXN0cmluZyA9IHJlcXVpcmUoJ3F1ZXJ5c3RyaW5nJyk7XG5pbXBvcnQgQ29uZmlnIGZyb20gJy4uLy4uL0NvbmZpZyc7XG5pbXBvcnQgRGVwcmVjYXRvciBmcm9tICcuLi8uLi9EZXByZWNhdG9yL0RlcHJlY2F0b3InO1xuXG4vLyBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IGZ1bGZpbGxzIGlmZiB0aGlzIHVzZXIgaWQgaXMgdmFsaWQuXG5mdW5jdGlvbiB2YWxpZGF0ZUF1dGhEYXRhKGF1dGhEYXRhLCBvcHRpb25zKSB7XG4gIGNvbnN0IGNvbmZpZyA9IENvbmZpZy5nZXQoUGFyc2UuYXBwbGljYXRpb25JZCk7XG5cbiAgRGVwcmVjYXRvci5sb2dSdW50aW1lRGVwcmVjYXRpb24oeyB1c2FnZTogJ2phbnJhaW5lbmdhZ2UgYWRhcHRlcicgfSk7XG4gIGlmICghY29uZmlnPy5hdXRoPy5qYW5yYWluZW5nYWdlPy5lbmFibGVJbnNlY3VyZUF1dGggfHwgIWNvbmZpZy5lbmFibGVJbnNlY3VyZUF1dGhBZGFwdGVycykge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihQYXJzZS5FcnJvci5JTlRFUk5BTF9TRVJWRVJfRVJST1IsICdqYW5yYWluZW5nYWdlIGFkYXB0ZXIgb25seSB3b3JrcyB3aXRoIGVuYWJsZUluc2VjdXJlQXV0aDogdHJ1ZScpO1xuICB9XG5cbiAgcmV0dXJuIGFwaVJlcXVlc3Qob3B0aW9ucy5hcGlfa2V5LCBhdXRoRGF0YS5hdXRoX3Rva2VuKS50aGVuKGRhdGEgPT4ge1xuICAgIC8vc3VjY2Vzc2Z1bCByZXNwb25zZSB3aWxsIGhhdmUgYSBcInN0YXRcIiAoc3RhdHVzKSBvZiAnb2snIGFuZCBhIHByb2ZpbGUgbm9kZSB3aXRoIGFuIGlkZW50aWZpZXJcbiAgICAvL3NlZTogaHR0cDovL2RldmVsb3BlcnMuamFucmFpbi5jb20vb3ZlcnZpZXcvc29jaWFsLWxvZ2luL2lkZW50aXR5LXByb3ZpZGVycy91c2VyLXByb2ZpbGUtZGF0YS8jbm9ybWFsaXplZC11c2VyLXByb2ZpbGUtZGF0YVxuICAgIGlmIChkYXRhICYmIGRhdGEuc3RhdCA9PSAnb2snICYmIGRhdGEucHJvZmlsZS5pZGVudGlmaWVyID09IGF1dGhEYXRhLmlkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihcbiAgICAgIFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsXG4gICAgICAnSmFucmFpbiBlbmdhZ2UgYXV0aCBpcyBpbnZhbGlkIGZvciB0aGlzIHVzZXIuJ1xuICAgICk7XG4gIH0pO1xufVxuXG4vLyBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IGZ1bGZpbGxzIGlmZiB0aGlzIGFwcCBpZCBpcyB2YWxpZC5cbmZ1bmN0aW9uIHZhbGlkYXRlQXBwSWQoKSB7XG4gIC8vbm8tb3BcbiAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xufVxuXG4vLyBBIHByb21pc2V5IHdyYXBwZXIgZm9yIGFwaSByZXF1ZXN0c1xuZnVuY3Rpb24gYXBpUmVxdWVzdChhcGlfa2V5LCBhdXRoX3Rva2VuKSB7XG4gIHZhciBwb3N0X2RhdGEgPSBxdWVyeXN0cmluZy5zdHJpbmdpZnkoe1xuICAgIHRva2VuOiBhdXRoX3Rva2VuLFxuICAgIGFwaUtleTogYXBpX2tleSxcbiAgICBmb3JtYXQ6ICdqc29uJyxcbiAgfSk7XG5cbiAgdmFyIHBvc3Rfb3B0aW9ucyA9IHtcbiAgICBob3N0OiAncnB4bm93LmNvbScsXG4gICAgcGF0aDogJy9hcGkvdjIvYXV0aF9pbmZvJyxcbiAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICBoZWFkZXJzOiB7XG4gICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcsXG4gICAgICAnQ29udGVudC1MZW5ndGgnOiBwb3N0X2RhdGEubGVuZ3RoLFxuICAgIH0sXG4gIH07XG5cbiAgcmV0dXJuIGh0dHBzUmVxdWVzdC5yZXF1ZXN0KHBvc3Rfb3B0aW9ucywgcG9zdF9kYXRhKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIHZhbGlkYXRlQXBwSWQ6IHZhbGlkYXRlQXBwSWQsXG4gIHZhbGlkYXRlQXV0aERhdGE6IHZhbGlkYXRlQXV0aERhdGEsXG59O1xuIl0sIm1hcHBpbmdzIjoiOztBQUlBLElBQUFBLE9BQUEsR0FBQUMsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFDLFdBQUEsR0FBQUYsc0JBQUEsQ0FBQUMsT0FBQTtBQUFxRCxTQUFBRCx1QkFBQUcsQ0FBQSxXQUFBQSxDQUFBLElBQUFBLENBQUEsQ0FBQUMsVUFBQSxHQUFBRCxDQUFBLEtBQUFFLE9BQUEsRUFBQUYsQ0FBQTtBQUxyRDtBQUNBLElBQUlHLFlBQVksR0FBR0wsT0FBTyxDQUFDLGdCQUFnQixDQUFDO0FBQzVDLElBQUlNLEtBQUssR0FBR04sT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDTSxLQUFLO0FBQ3ZDLElBQUlDLFdBQVcsR0FBR1AsT0FBTyxDQUFDLGFBQWEsQ0FBQztBQUl4QztBQUNBLFNBQVNRLGdCQUFnQkEsQ0FBQ0MsUUFBUSxFQUFFQyxPQUFPLEVBQUU7RUFDM0MsTUFBTUMsTUFBTSxHQUFHQyxlQUFNLENBQUNDLEdBQUcsQ0FBQ1AsS0FBSyxDQUFDUSxhQUFhLENBQUM7RUFFOUNDLG1CQUFVLENBQUNDLHFCQUFxQixDQUFDO0lBQUVDLEtBQUssRUFBRTtFQUF3QixDQUFDLENBQUM7RUFDcEUsSUFBSSxDQUFDTixNQUFNLEVBQUVPLElBQUksRUFBRUMsYUFBYSxFQUFFQyxrQkFBa0IsSUFBSSxDQUFDVCxNQUFNLENBQUNVLDBCQUEwQixFQUFFO0lBQzFGLE1BQU0sSUFBSWYsS0FBSyxDQUFDZ0IsS0FBSyxDQUFDaEIsS0FBSyxDQUFDZ0IsS0FBSyxDQUFDQyxxQkFBcUIsRUFBRSxnRUFBZ0UsQ0FBQztFQUM1SDtFQUVBLE9BQU9DLFVBQVUsQ0FBQ2QsT0FBTyxDQUFDZSxPQUFPLEVBQUVoQixRQUFRLENBQUNpQixVQUFVLENBQUMsQ0FBQ0MsSUFBSSxDQUFDQyxJQUFJLElBQUk7SUFDbkU7SUFDQTtJQUNBLElBQUlBLElBQUksSUFBSUEsSUFBSSxDQUFDQyxJQUFJLElBQUksSUFBSSxJQUFJRCxJQUFJLENBQUNFLE9BQU8sQ0FBQ0MsVUFBVSxJQUFJdEIsUUFBUSxDQUFDdUIsRUFBRSxFQUFFO01BQ3ZFO0lBQ0Y7SUFDQSxNQUFNLElBQUkxQixLQUFLLENBQUNnQixLQUFLLENBQ25CaEIsS0FBSyxDQUFDZ0IsS0FBSyxDQUFDVyxnQkFBZ0IsRUFDNUIsK0NBQ0YsQ0FBQztFQUNILENBQUMsQ0FBQztBQUNKOztBQUVBO0FBQ0EsU0FBU0MsYUFBYUEsQ0FBQSxFQUFHO0VBQ3ZCO0VBQ0EsT0FBT0MsT0FBTyxDQUFDQyxPQUFPLENBQUMsQ0FBQztBQUMxQjs7QUFFQTtBQUNBLFNBQVNaLFVBQVVBLENBQUNDLE9BQU8sRUFBRUMsVUFBVSxFQUFFO0VBQ3ZDLElBQUlXLFNBQVMsR0FBRzlCLFdBQVcsQ0FBQytCLFNBQVMsQ0FBQztJQUNwQ0MsS0FBSyxFQUFFYixVQUFVO0lBQ2pCYyxNQUFNLEVBQUVmLE9BQU87SUFDZmdCLE1BQU0sRUFBRTtFQUNWLENBQUMsQ0FBQztFQUVGLElBQUlDLFlBQVksR0FBRztJQUNqQkMsSUFBSSxFQUFFLFlBQVk7SUFDbEJDLElBQUksRUFBRSxtQkFBbUI7SUFDekJDLE1BQU0sRUFBRSxNQUFNO0lBQ2RDLE9BQU8sRUFBRTtNQUNQLGNBQWMsRUFBRSxtQ0FBbUM7TUFDbkQsZ0JBQWdCLEVBQUVULFNBQVMsQ0FBQ1U7SUFDOUI7RUFDRixDQUFDO0VBRUQsT0FBTzFDLFlBQVksQ0FBQzJDLE9BQU8sQ0FBQ04sWUFBWSxFQUFFTCxTQUFTLENBQUM7QUFDdEQ7QUFFQVksTUFBTSxDQUFDQyxPQUFPLEdBQUc7RUFDZmhCLGFBQWEsRUFBRUEsYUFBYTtFQUM1QjFCLGdCQUFnQixFQUFFQTtBQUNwQixDQUFDIiwiaWdub3JlTGlzdCI6W119
|
|
@@ -0,0 +1,148 @@
|
|
|
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 httpsRequest = require('./httpsRequest');
|
|
75
|
+
const arraysEqual = (_arr1, _arr2) => {
|
|
76
|
+
if (!Array.isArray(_arr1) || !Array.isArray(_arr2) || _arr1.length !== _arr2.length) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
var arr1 = _arr1.concat().sort();
|
|
80
|
+
var arr2 = _arr2.concat().sort();
|
|
81
|
+
for (var i = 0; i < arr1.length; i++) {
|
|
82
|
+
if (arr1[i] !== arr2[i]) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
};
|
|
88
|
+
const handleAuth = async ({
|
|
89
|
+
access_token,
|
|
90
|
+
id,
|
|
91
|
+
roles,
|
|
92
|
+
groups
|
|
93
|
+
} = {}, {
|
|
94
|
+
config
|
|
95
|
+
} = {}) => {
|
|
96
|
+
if (!(access_token && id)) {
|
|
97
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Missing access token and/or User id');
|
|
98
|
+
}
|
|
99
|
+
if (!config || !(config['auth-server-url'] && config['realm'])) {
|
|
100
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Missing keycloak configuration');
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const response = await httpsRequest.get({
|
|
104
|
+
host: config['auth-server-url'],
|
|
105
|
+
path: `/realms/${config['realm']}/protocol/openid-connect/userinfo`,
|
|
106
|
+
headers: {
|
|
107
|
+
Authorization: 'Bearer ' + access_token
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
if (response && response.data && response.data.sub == id && arraysEqual(response.data.roles, roles) && arraysEqual(response.data.groups, groups)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid authentication');
|
|
114
|
+
} catch (e) {
|
|
115
|
+
if (e instanceof Parse.Error) {
|
|
116
|
+
throw e;
|
|
117
|
+
}
|
|
118
|
+
const error = JSON.parse(e.text);
|
|
119
|
+
if (error.error_description) {
|
|
120
|
+
throw new Parse.Error(Parse.Error.HOSTING_ERROR, error.error_description);
|
|
121
|
+
} else {
|
|
122
|
+
throw new Parse.Error(Parse.Error.HOSTING_ERROR, 'Could not connect to the authentication server');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
@param {Object} authData: the client provided authData
|
|
129
|
+
@param {string} authData.access_token: the access_token retrieved from client authentication in Keycloak
|
|
130
|
+
@param {string} authData.id: the id retrieved from client authentication in Keycloak
|
|
131
|
+
@param {Array} authData.roles: the roles retrieved from client authentication in Keycloak
|
|
132
|
+
@param {Array} authData.groups: the groups retrieved from client authentication in Keycloak
|
|
133
|
+
@param {Object} options: additional options
|
|
134
|
+
@param {Object} options.config: the config object passed during Parse Server instantiation
|
|
135
|
+
*/
|
|
136
|
+
function validateAuthData(authData, options = {}) {
|
|
137
|
+
return handleAuth(authData, options);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Returns a promise that fulfills if this app id is valid.
|
|
141
|
+
function validateAppId() {
|
|
142
|
+
return Promise.resolve();
|
|
143
|
+
}
|
|
144
|
+
module.exports = {
|
|
145
|
+
validateAppId,
|
|
146
|
+
validateAuthData
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJQYXJzZSIsInJlcXVpcmUiLCJodHRwc1JlcXVlc3QiLCJhcnJheXNFcXVhbCIsIl9hcnIxIiwiX2FycjIiLCJBcnJheSIsImlzQXJyYXkiLCJsZW5ndGgiLCJhcnIxIiwiY29uY2F0Iiwic29ydCIsImFycjIiLCJpIiwiaGFuZGxlQXV0aCIsImFjY2Vzc190b2tlbiIsImlkIiwicm9sZXMiLCJncm91cHMiLCJjb25maWciLCJFcnJvciIsIk9CSkVDVF9OT1RfRk9VTkQiLCJyZXNwb25zZSIsImdldCIsImhvc3QiLCJwYXRoIiwiaGVhZGVycyIsIkF1dGhvcml6YXRpb24iLCJkYXRhIiwic3ViIiwiZSIsImVycm9yIiwiSlNPTiIsInBhcnNlIiwidGV4dCIsImVycm9yX2Rlc2NyaXB0aW9uIiwiSE9TVElOR19FUlJPUiIsInZhbGlkYXRlQXV0aERhdGEiLCJhdXRoRGF0YSIsIm9wdGlvbnMiLCJ2YWxpZGF0ZUFwcElkIiwiUHJvbWlzZSIsInJlc29sdmUiLCJtb2R1bGUiLCJleHBvcnRzIl0sInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL0FkYXB0ZXJzL0F1dGgva2V5Y2xvYWsuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBQYXJzZSBTZXJ2ZXIgYXV0aGVudGljYXRpb24gYWRhcHRlciBmb3IgS2V5Y2xvYWsuXG4gKlxuICogQGNsYXNzIEtleWNsb2FrQWRhcHRlclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgYWRhcHRlciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucy5jb25maWcgLSBUaGUgS2V5Y2xvYWsgY29uZmlndXJhdGlvbiBvYmplY3QsIHR5cGljYWxseSBsb2FkZWQgZnJvbSBhIEpTT04gZmlsZS5cbiAqIEBwYXJhbSB7U3RyaW5nfSBvcHRpb25zLmNvbmZpZy5hdXRoLXNlcnZlci11cmwgLSBUaGUgS2V5Y2xvYWsgYXV0aGVudGljYXRpb24gc2VydmVyIFVSTC5cbiAqIEBwYXJhbSB7U3RyaW5nfSBvcHRpb25zLmNvbmZpZy5yZWFsbSAtIFRoZSBLZXljbG9hayByZWFsbSBuYW1lLlxuICogQHBhcmFtIHtTdHJpbmd9IG9wdGlvbnMuY29uZmlnLmNsaWVudC1pZCAtIFRoZSBLZXljbG9hayBjbGllbnQgSUQuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IGF1dGhEYXRhIC0gVGhlIGF1dGhlbnRpY2F0aW9uIGRhdGEgcHJvdmlkZWQgYnkgdGhlIGNsaWVudC5cbiAqIEBwYXJhbSB7U3RyaW5nfSBhdXRoRGF0YS5hY2Nlc3NfdG9rZW4gLSBUaGUgS2V5Y2xvYWsgYWNjZXNzIHRva2VuIHJldHJpZXZlZCBkdXJpbmcgY2xpZW50IGF1dGhlbnRpY2F0aW9uLlxuICogQHBhcmFtIHtTdHJpbmd9IGF1dGhEYXRhLmlkIC0gVGhlIHVzZXIgSUQgcmV0cmlldmVkIGZyb20gS2V5Y2xvYWsgZHVyaW5nIGNsaWVudCBhdXRoZW50aWNhdGlvbi5cbiAqIEBwYXJhbSB7QXJyYXl9IFthdXRoRGF0YS5yb2xlc10gLSBUaGUgcm9sZXMgYXNzaWduZWQgdG8gdGhlIHVzZXIgaW4gS2V5Y2xvYWsgKG9wdGlvbmFsKS5cbiAqIEBwYXJhbSB7QXJyYXl9IFthdXRoRGF0YS5ncm91cHNdIC0gVGhlIGdyb3VwcyBhc3NpZ25lZCB0byB0aGUgdXNlciBpbiBLZXljbG9hayAob3B0aW9uYWwpLlxuICpcbiAqIEBkZXNjcmlwdGlvblxuICogIyMgUGFyc2UgU2VydmVyIENvbmZpZ3VyYXRpb25cbiAqIFRvIGNvbmZpZ3VyZSBQYXJzZSBTZXJ2ZXIgZm9yIEtleWNsb2FrIGF1dGhlbnRpY2F0aW9uLCB1c2UgdGhlIGZvbGxvd2luZyBzdHJ1Y3R1cmU6XG4gKiBgYGBqYXZhc2NyaXB0XG4gKiB7XG4gKiAgIFwiYXV0aFwiOiB7XG4gKiAgICAgXCJrZXljbG9ha1wiOiB7XG4gKiAgICAgICBcImNvbmZpZ1wiOiByZXF1aXJlKCcuL2F1dGgva2V5Y2xvYWsuanNvbicpXG4gKiAgICAgfVxuICogICB9XG4gKiB9XG4gKiBgYGBcbiAqIEVuc3VyZSB0aGUgYGtleWNsb2FrLmpzb25gIGNvbmZpZ3VyYXRpb24gZmlsZSBpcyBnZW5lcmF0ZWQgZnJvbSBLZXljbG9haydzIHNldHVwIGd1aWRlIGFuZCBpbmNsdWRlczpcbiAqIC0gYGF1dGgtc2VydmVyLXVybGA6IFRoZSBLZXljbG9hayBhdXRoZW50aWNhdGlvbiBzZXJ2ZXIgVVJMLlxuICogLSBgcmVhbG1gOiBUaGUgS2V5Y2xvYWsgcmVhbG0gbmFtZS5cbiAqIC0gYGNsaWVudC1pZGA6IFRoZSBLZXljbG9hayBjbGllbnQgSUQuXG4gKlxuICogIyMgQXV0aCBEYXRhXG4gKiBUaGUgYWRhcHRlciByZXF1aXJlcyB0aGUgZm9sbG93aW5nIGBhdXRoRGF0YWAgZmllbGRzOlxuICogLSBgYWNjZXNzX3Rva2VuYDogVGhlIEtleWNsb2FrIGFjY2VzcyB0b2tlbiByZXRyaWV2ZWQgZHVyaW5nIGNsaWVudCBhdXRoZW50aWNhdGlvbi5cbiAqIC0gYGlkYDogVGhlIHVzZXIgSUQgcmV0cmlldmVkIGZyb20gS2V5Y2xvYWsgZHVyaW5nIGNsaWVudCBhdXRoZW50aWNhdGlvbi5cbiAqIC0gYHJvbGVzYCAob3B0aW9uYWwpOiBUaGUgcm9sZXMgYXNzaWduZWQgdG8gdGhlIHVzZXIgaW4gS2V5Y2xvYWsuXG4gKiAtIGBncm91cHNgIChvcHRpb25hbCk6IFRoZSBncm91cHMgYXNzaWduZWQgdG8gdGhlIHVzZXIgaW4gS2V5Y2xvYWsuXG4gKlxuICogIyMgQXV0aCBQYXlsb2FkIEV4YW1wbGVcbiAqICMjIyBFeGFtcGxlIEF1dGggRGF0YVxuICogYGBganNvblxuICoge1xuICogICBcImtleWNsb2FrXCI6IHtcbiAqICAgICBcImFjY2Vzc190b2tlblwiOiBcImFuIGF1dGhvcml6ZWQgS2V5Y2xvYWsgYWNjZXNzIHRva2VuIGZvciB0aGUgdXNlclwiLFxuICogICAgIFwiaWRcIjogXCJ1c2VyJ3MgS2V5Y2xvYWsgSUQgYXMgYSBzdHJpbmdcIixcbiAqICAgICBcInJvbGVzXCI6IFtcImFkbWluXCIsIFwidXNlclwiXSxcbiAqICAgICBcImdyb3Vwc1wiOiBbXCJncm91cDFcIiwgXCJncm91cDJcIl1cbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogIyMgTm90ZXNcbiAqIC0gUGFyc2UgU2VydmVyIHZhbGlkYXRlcyB0aGUgcHJvdmlkZWQgYGF1dGhEYXRhYCBieSBtYWtpbmcgYSBgdXNlcmluZm9gIGNhbGwgdG8gS2V5Y2xvYWsgYW5kIGVuc3VyZXMgdGhlIGF0dHJpYnV0ZXMgbWF0Y2ggdGhvc2UgcmV0dXJuZWQgYnkgS2V5Y2xvYWsuXG4gKlxuICogIyMgS2V5Y2xvYWsgQ29uZmlndXJhdGlvblxuICogVG8gY29uZmlndXJlIEtleWNsb2FrLCBjb3B5IHRoZSBKU09OIGNvbmZpZ3VyYXRpb24gZmlsZSBnZW5lcmF0ZWQgZnJvbSBLZXljbG9haydzIHNldHVwIGd1aWRlOlxuICogLSBbS2V5Y2xvYWsgU2VjdXJpbmcgQXBwcyBEb2N1bWVudGF0aW9uXShodHRwczovL3d3dy5rZXljbG9hay5vcmcvZG9jcy9sYXRlc3Qvc2VjdXJpbmdfYXBwcy9pbmRleC5odG1sI19qYXZhc2NyaXB0X2FkYXB0ZXIpXG4gKlxuICogUGxhY2UgdGhlIGNvbmZpZ3VyYXRpb24gZmlsZSBvbiB5b3VyIHNlcnZlciwgZm9yIGV4YW1wbGU6XG4gKiAtIGBhdXRoL2tleWNsb2FrLmpzb25gXG4gKlxuICogRm9yIG1vcmUgaW5mb3JtYXRpb24gb24gS2V5Y2xvYWsgYXV0aGVudGljYXRpb24sIHNlZTpcbiAqIC0gW1NlY3VyaW5nIEFwcHMgRG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly93d3cua2V5Y2xvYWsub3JnL2RvY3MvbGF0ZXN0L3NlY3VyaW5nX2FwcHMvKVxuICogLSBbU2VydmVyIEFkbWluaXN0cmF0aW9uIERvY3VtZW50YXRpb25dKGh0dHBzOi8vd3d3LmtleWNsb2FrLm9yZy9kb2NzL2xhdGVzdC9zZXJ2ZXJfYWRtaW4vKVxuICovXG5cbmNvbnN0IHsgUGFyc2UgfSA9IHJlcXVpcmUoJ3BhcnNlL25vZGUnKTtcbmNvbnN0IGh0dHBzUmVxdWVzdCA9IHJlcXVpcmUoJy4vaHR0cHNSZXF1ZXN0Jyk7XG5cbmNvbnN0IGFycmF5c0VxdWFsID0gKF9hcnIxLCBfYXJyMikgPT4ge1xuICBpZiAoIUFycmF5LmlzQXJyYXkoX2FycjEpIHx8ICFBcnJheS5pc0FycmF5KF9hcnIyKSB8fCBfYXJyMS5sZW5ndGggIT09IF9hcnIyLmxlbmd0aCkgeyByZXR1cm4gZmFsc2U7IH1cblxuICB2YXIgYXJyMSA9IF9hcnIxLmNvbmNhdCgpLnNvcnQoKTtcbiAgdmFyIGFycjIgPSBfYXJyMi5jb25jYXQoKS5zb3J0KCk7XG5cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBhcnIxLmxlbmd0aDsgaSsrKSB7XG4gICAgaWYgKGFycjFbaV0gIT09IGFycjJbaV0pIHsgcmV0dXJuIGZhbHNlOyB9XG4gIH1cblxuICByZXR1cm4gdHJ1ZTtcbn07XG5cbmNvbnN0IGhhbmRsZUF1dGggPSBhc3luYyAoeyBhY2Nlc3NfdG9rZW4sIGlkLCByb2xlcywgZ3JvdXBzIH0gPSB7fSwgeyBjb25maWcgfSA9IHt9KSA9PiB7XG4gIGlmICghKGFjY2Vzc190b2tlbiAmJiBpZCkpIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCwgJ01pc3NpbmcgYWNjZXNzIHRva2VuIGFuZC9vciBVc2VyIGlkJyk7XG4gIH1cbiAgaWYgKCFjb25maWcgfHwgIShjb25maWdbJ2F1dGgtc2VydmVyLXVybCddICYmIGNvbmZpZ1sncmVhbG0nXSkpIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCwgJ01pc3Npbmcga2V5Y2xvYWsgY29uZmlndXJhdGlvbicpO1xuICB9XG4gIHRyeSB7XG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBodHRwc1JlcXVlc3QuZ2V0KHtcbiAgICAgIGhvc3Q6IGNvbmZpZ1snYXV0aC1zZXJ2ZXItdXJsJ10sXG4gICAgICBwYXRoOiBgL3JlYWxtcy8ke2NvbmZpZ1sncmVhbG0nXX0vcHJvdG9jb2wvb3BlbmlkLWNvbm5lY3QvdXNlcmluZm9gLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICBBdXRob3JpemF0aW9uOiAnQmVhcmVyICcgKyBhY2Nlc3NfdG9rZW4sXG4gICAgICB9LFxuICAgIH0pO1xuICAgIGlmIChcbiAgICAgIHJlc3BvbnNlICYmXG4gICAgICByZXNwb25zZS5kYXRhICYmXG4gICAgICByZXNwb25zZS5kYXRhLnN1YiA9PSBpZCAmJlxuICAgICAgYXJyYXlzRXF1YWwocmVzcG9uc2UuZGF0YS5yb2xlcywgcm9sZXMpICYmXG4gICAgICBhcnJheXNFcXVhbChyZXNwb25zZS5kYXRhLmdyb3VwcywgZ3JvdXBzKVxuICAgICkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCwgJ0ludmFsaWQgYXV0aGVudGljYXRpb24nKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGlmIChlIGluc3RhbmNlb2YgUGFyc2UuRXJyb3IpIHtcbiAgICAgIHRocm93IGU7XG4gICAgfVxuICAgIGNvbnN0IGVycm9yID0gSlNPTi5wYXJzZShlLnRleHQpO1xuICAgIGlmIChlcnJvci5lcnJvcl9kZXNjcmlwdGlvbikge1xuICAgICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLkhPU1RJTkdfRVJST1IsIGVycm9yLmVycm9yX2Rlc2NyaXB0aW9uKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFxuICAgICAgICBQYXJzZS5FcnJvci5IT1NUSU5HX0VSUk9SLFxuICAgICAgICAnQ291bGQgbm90IGNvbm5lY3QgdG8gdGhlIGF1dGhlbnRpY2F0aW9uIHNlcnZlcidcbiAgICAgICk7XG4gICAgfVxuICB9XG59O1xuXG4vKlxuICBAcGFyYW0ge09iamVjdH0gYXV0aERhdGE6IHRoZSBjbGllbnQgcHJvdmlkZWQgYXV0aERhdGFcbiAgQHBhcmFtIHtzdHJpbmd9IGF1dGhEYXRhLmFjY2Vzc190b2tlbjogdGhlIGFjY2Vzc190b2tlbiByZXRyaWV2ZWQgZnJvbSBjbGllbnQgYXV0aGVudGljYXRpb24gaW4gS2V5Y2xvYWtcbiAgQHBhcmFtIHtzdHJpbmd9IGF1dGhEYXRhLmlkOiB0aGUgaWQgcmV0cmlldmVkIGZyb20gY2xpZW50IGF1dGhlbnRpY2F0aW9uIGluIEtleWNsb2FrXG4gIEBwYXJhbSB7QXJyYXl9ICBhdXRoRGF0YS5yb2xlczogdGhlIHJvbGVzIHJldHJpZXZlZCBmcm9tIGNsaWVudCBhdXRoZW50aWNhdGlvbiBpbiBLZXljbG9ha1xuICBAcGFyYW0ge0FycmF5fSAgYXV0aERhdGEuZ3JvdXBzOiB0aGUgZ3JvdXBzIHJldHJpZXZlZCBmcm9tIGNsaWVudCBhdXRoZW50aWNhdGlvbiBpbiBLZXljbG9ha1xuICBAcGFyYW0ge09iamVjdH0gb3B0aW9uczogYWRkaXRpb25hbCBvcHRpb25zXG4gIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zLmNvbmZpZzogdGhlIGNvbmZpZyBvYmplY3QgcGFzc2VkIGR1cmluZyBQYXJzZSBTZXJ2ZXIgaW5zdGFudGlhdGlvblxuKi9cbmZ1bmN0aW9uIHZhbGlkYXRlQXV0aERhdGEoYXV0aERhdGEsIG9wdGlvbnMgPSB7fSkge1xuICByZXR1cm4gaGFuZGxlQXV0aChhdXRoRGF0YSwgb3B0aW9ucyk7XG59XG5cbi8vIFJldHVybnMgYSBwcm9taXNlIHRoYXQgZnVsZmlsbHMgaWYgdGhpcyBhcHAgaWQgaXMgdmFsaWQuXG5mdW5jdGlvbiB2YWxpZGF0ZUFwcElkKCkge1xuICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICB2YWxpZGF0ZUFwcElkLFxuICB2YWxpZGF0ZUF1dGhEYXRhLFxufTtcbiJdLCJtYXBwaW5ncyI6Ijs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD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
|
|
@@ -0,0 +1,167 @@
|
|
|
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
|
+
function validateAuthData(authData, options) {
|
|
80
|
+
if (!optionsAreValid(options)) {
|
|
81
|
+
return new Promise((_, reject) => {
|
|
82
|
+
reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP auth configuration missing'));
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const clientOptions = options.url.startsWith('ldaps://') ? {
|
|
86
|
+
url: options.url,
|
|
87
|
+
tlsOptions: options.tlsOptions
|
|
88
|
+
} : {
|
|
89
|
+
url: options.url
|
|
90
|
+
};
|
|
91
|
+
const client = ldapjs.createClient(clientOptions);
|
|
92
|
+
const userCn = typeof options.dn === 'string' ? options.dn.replace('{{id}}', authData.id) : `uid=${authData.id},${options.suffix}`;
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
client.bind(userCn, authData.password, ldapError => {
|
|
95
|
+
delete authData.password;
|
|
96
|
+
if (ldapError) {
|
|
97
|
+
let error;
|
|
98
|
+
switch (ldapError.code) {
|
|
99
|
+
case 49:
|
|
100
|
+
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAP: Wrong username or password');
|
|
101
|
+
break;
|
|
102
|
+
case 'DEPTH_ZERO_SELF_SIGNED_CERT':
|
|
103
|
+
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAPS: Certificate mismatch');
|
|
104
|
+
break;
|
|
105
|
+
default:
|
|
106
|
+
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAP: Somthing went wrong (' + ldapError.code + ')');
|
|
107
|
+
}
|
|
108
|
+
reject(error);
|
|
109
|
+
client.destroy(ldapError);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (typeof options.groupCn === 'string' && typeof options.groupFilter === 'string') {
|
|
113
|
+
searchForGroup(client, options, authData.id, resolve, reject);
|
|
114
|
+
} else {
|
|
115
|
+
client.unbind();
|
|
116
|
+
client.destroy();
|
|
117
|
+
resolve();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function optionsAreValid(options) {
|
|
123
|
+
return typeof options === 'object' && typeof options.suffix === 'string' && typeof options.url === 'string' && (options.url.startsWith('ldap://') || options.url.startsWith('ldaps://') && typeof options.tlsOptions === 'object');
|
|
124
|
+
}
|
|
125
|
+
function searchForGroup(client, options, id, resolve, reject) {
|
|
126
|
+
const filter = options.groupFilter.replace(/{{id}}/gi, id);
|
|
127
|
+
const opts = {
|
|
128
|
+
scope: 'sub',
|
|
129
|
+
filter: filter
|
|
130
|
+
};
|
|
131
|
+
let found = false;
|
|
132
|
+
client.search(options.suffix, opts, (searchError, res) => {
|
|
133
|
+
if (searchError) {
|
|
134
|
+
client.unbind();
|
|
135
|
+
client.destroy();
|
|
136
|
+
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
|
|
137
|
+
}
|
|
138
|
+
res.on('searchEntry', entry => {
|
|
139
|
+
if (entry.pojo.attributes.find(obj => obj.type === 'cn').values.includes(options.groupCn)) {
|
|
140
|
+
found = true;
|
|
141
|
+
client.unbind();
|
|
142
|
+
client.destroy();
|
|
143
|
+
return resolve();
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
res.on('end', () => {
|
|
147
|
+
if (!found) {
|
|
148
|
+
client.unbind();
|
|
149
|
+
client.destroy();
|
|
150
|
+
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP: User not in group'));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
res.on('error', () => {
|
|
154
|
+
client.unbind();
|
|
155
|
+
client.destroy();
|
|
156
|
+
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function validateAppId() {
|
|
161
|
+
return Promise.resolve();
|
|
162
|
+
}
|
|
163
|
+
module.exports = {
|
|
164
|
+
validateAppId,
|
|
165
|
+
validateAuthData
|
|
166
|
+
};
|
|
167
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJsZGFwanMiLCJyZXF1aXJlIiwiUGFyc2UiLCJ2YWxpZGF0ZUF1dGhEYXRhIiwiYXV0aERhdGEiLCJvcHRpb25zIiwib3B0aW9uc0FyZVZhbGlkIiwiUHJvbWlzZSIsIl8iLCJyZWplY3QiLCJFcnJvciIsIklOVEVSTkFMX1NFUlZFUl9FUlJPUiIsImNsaWVudE9wdGlvbnMiLCJ1cmwiLCJzdGFydHNXaXRoIiwidGxzT3B0aW9ucyIsImNsaWVudCIsImNyZWF0ZUNsaWVudCIsInVzZXJDbiIsImRuIiwicmVwbGFjZSIsImlkIiwic3VmZml4IiwicmVzb2x2ZSIsImJpbmQiLCJwYXNzd29yZCIsImxkYXBFcnJvciIsImVycm9yIiwiY29kZSIsIk9CSkVDVF9OT1RfRk9VTkQiLCJkZXN0cm95IiwiZ3JvdXBDbiIsImdyb3VwRmlsdGVyIiwic2VhcmNoRm9yR3JvdXAiLCJ1bmJpbmQiLCJmaWx0ZXIiLCJvcHRzIiwic2NvcGUiLCJmb3VuZCIsInNlYXJjaCIsInNlYXJjaEVycm9yIiwicmVzIiwib24iLCJlbnRyeSIsInBvam8iLCJhdHRyaWJ1dGVzIiwiZmluZCIsIm9iaiIsInR5cGUiLCJ2YWx1ZXMiLCJpbmNsdWRlcyIsInZhbGlkYXRlQXBwSWQiLCJtb2R1bGUiLCJleHBvcnRzIl0sInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL0FkYXB0ZXJzL0F1dGgvbGRhcC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBhcnNlIFNlcnZlciBhdXRoZW50aWNhdGlvbiBhZGFwdGVyIGZvciBMREFQLlxuICpcbiAqIEBjbGFzcyBMREFQXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBhZGFwdGVyIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAqIEBwYXJhbSB7U3RyaW5nfSBvcHRpb25zLnVybCAtIFRoZSBMREFQIHNlcnZlciBVUkwuIE11c3Qgc3RhcnQgd2l0aCBgbGRhcDovL2Agb3IgYGxkYXBzOi8vYC5cbiAqIEBwYXJhbSB7U3RyaW5nfSBvcHRpb25zLnN1ZmZpeCAtIFRoZSBMREFQIHN1ZmZpeCBmb3IgdXNlciBkaXN0aW5ndWlzaGVkIG5hbWVzIChETikuXG4gKiBAcGFyYW0ge1N0cmluZ30gW29wdGlvbnMuZG5dIC0gVGhlIGRpc3Rpbmd1aXNoZWQgbmFtZSAoRE4pIHRlbXBsYXRlIGZvciB1c2VyIGF1dGhlbnRpY2F0aW9uLiBSZXBsYWNlIGB7e2lkfX1gIHdpdGggdGhlIHVzZXJuYW1lLlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zLnRsc09wdGlvbnNdIC0gT3B0aW9ucyBmb3IgTERBUFMgVExTIGNvbm5lY3Rpb25zLlxuICogQHBhcmFtIHtTdHJpbmd9IFtvcHRpb25zLmdyb3VwQ25dIC0gVGhlIGNvbW1vbiBuYW1lIChDTikgb2YgdGhlIGdyb3VwIHRvIHZlcmlmeSB1c2VyIG1lbWJlcnNoaXAuXG4gKiBAcGFyYW0ge1N0cmluZ30gW29wdGlvbnMuZ3JvdXBGaWx0ZXJdIC0gVGhlIExEQVAgc2VhcmNoIGZpbHRlciBmb3IgZ3JvdXBzLCB3aXRoIGB7e2lkfX1gIHJlcGxhY2VkIGJ5IHRoZSB1c2VybmFtZS5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gYXV0aERhdGEgLSBUaGUgYXV0aGVudGljYXRpb24gZGF0YSBwcm92aWRlZCBieSB0aGUgY2xpZW50LlxuICogQHBhcmFtIHtTdHJpbmd9IGF1dGhEYXRhLmlkIC0gVGhlIHVzZXIncyBMREFQIHVzZXJuYW1lLlxuICogQHBhcmFtIHtTdHJpbmd9IGF1dGhEYXRhLnBhc3N3b3JkIC0gVGhlIHVzZXIncyBMREFQIHBhc3N3b3JkLlxuICpcbiAqIEBkZXNjcmlwdGlvblxuICogIyMgUGFyc2UgU2VydmVyIENvbmZpZ3VyYXRpb25cbiAqIFRvIGNvbmZpZ3VyZSBQYXJzZSBTZXJ2ZXIgZm9yIExEQVAgYXV0aGVudGljYXRpb24sIHVzZSB0aGUgZm9sbG93aW5nIHN0cnVjdHVyZTpcbiAqIGBgYGphdmFzY3JpcHRcbiAqIHtcbiAqICAgYXV0aDoge1xuICogICAgIGxkYXA6IHtcbiAqICAgICAgIHVybDogJ2xkYXBzOi8vbGRhcC5leGFtcGxlLmNvbScsXG4gKiAgICAgICBzdWZmaXg6ICdvdT11c2VycyxkYz1leGFtcGxlLGRjPWNvbScsXG4gKiAgICAgICBncm91cENuOiAnYWRtaW5zJyxcbiAqICAgICAgIGdyb3VwRmlsdGVyOiAnKG1lbWJlclVpZD17e2lkfX0pJyxcbiAqICAgICAgIHRsc09wdGlvbnM6IHtcbiAqICAgICAgICAgcmVqZWN0VW5hdXRob3JpemVkOiBmYWxzZVxuICogICAgICAgfVxuICogICAgIH1cbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogIyMgQXV0aGVudGljYXRpb24gUHJvY2Vzc1xuICogMS4gVmFsaWRhdGVzIHRoZSBwcm92aWRlZCBgYXV0aERhdGFgIHVzaW5nIGFuIExEQVAgYmluZCBvcGVyYXRpb24uXG4gKiAyLiBPcHRpb25hbGx5LCB2ZXJpZmllcyB0aGF0IHRoZSB1c2VyIGJlbG9uZ3MgdG8gYSBzcGVjaWZpYyBncm91cCBieSBwZXJmb3JtaW5nIGFuIExEQVAgc2VhcmNoIHVzaW5nIHRoZSBwcm92aWRlZCBgZ3JvdXBDbmAgb3IgYGdyb3VwRmlsdGVyYC5cbiAqXG4gKiAjIyBBdXRoIFBheWxvYWRcbiAqIFRoZSBhZGFwdGVyIHJlcXVpcmVzIHRoZSBmb2xsb3dpbmcgYGF1dGhEYXRhYCBmaWVsZHM6XG4gKiAtIGBpZGA6IFRoZSB1c2VyJ3MgTERBUCB1c2VybmFtZS5cbiAqIC0gYHBhc3N3b3JkYDogVGhlIHVzZXIncyBMREFQIHBhc3N3b3JkLlxuICpcbiAqICMjIyBFeGFtcGxlIEF1dGggUGF5bG9hZFxuICogYGBganNvblxuICoge1xuICogICBcImxkYXBcIjoge1xuICogICAgIFwiaWRcIjogXCJqZG9lXCIsXG4gKiAgICAgXCJwYXNzd29yZFwiOiBcInBhc3N3b3JkMTIzXCJcbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogQGV4YW1wbGUgPGNhcHRpb24+Q29uZmlndXJhdGlvbiBFeGFtcGxlPC9jYXB0aW9uPlxuICogLy8gRXhhbXBsZSBQYXJzZSBTZXJ2ZXIgY29uZmlndXJhdGlvbjpcbiAqIGNvbnN0IGNvbmZpZyA9IHtcbiAqICAgYXV0aDoge1xuICogICAgIGxkYXA6IHtcbiAqICAgICAgIHVybDogJ2xkYXBzOi8vbGRhcC5leGFtcGxlLmNvbScsXG4gKiAgICAgICBzdWZmaXg6ICdvdT11c2VycyxkYz1leGFtcGxlLGRjPWNvbScsXG4gKiAgICAgICBncm91cENuOiAnYWRtaW5zJyxcbiAqICAgICAgIGdyb3VwRmlsdGVyOiAnKG1lbWJlclVpZD17e2lkfX0pJyxcbiAqICAgICAgIHRsc09wdGlvbnM6IHtcbiAqICAgICAgICAgcmVqZWN0VW5hdXRob3JpemVkOiBmYWxzZVxuICogICAgICAgfVxuICogICAgIH1cbiAqICAgfVxuICogfTtcbiAqXG4gKiBAc2VlIHtAbGluayBodHRwczovL2xkYXAuY29tLyBMREFQIEJhc2ljc31cbiAqIEBzZWUge0BsaW5rIGh0dHBzOi8vbGRhcC5jb20vbGRhcC1maWx0ZXJzLyBMREFQIEZpbHRlcnN9XG4gKi9cblxuXG5jb25zdCBsZGFwanMgPSByZXF1aXJlKCdsZGFwanMnKTtcbmNvbnN0IFBhcnNlID0gcmVxdWlyZSgncGFyc2Uvbm9kZScpLlBhcnNlO1xuXG5mdW5jdGlvbiB2YWxpZGF0ZUF1dGhEYXRhKGF1dGhEYXRhLCBvcHRpb25zKSB7XG4gIGlmICghb3B0aW9uc0FyZVZhbGlkKG9wdGlvbnMpKSB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChfLCByZWplY3QpID0+IHtcbiAgICAgIHJlamVjdChuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuSU5URVJOQUxfU0VSVkVSX0VSUk9SLCAnTERBUCBhdXRoIGNvbmZpZ3VyYXRpb24gbWlzc2luZycpKTtcbiAgICB9KTtcbiAgfVxuICBjb25zdCBjbGllbnRPcHRpb25zID0gb3B0aW9ucy51cmwuc3RhcnRzV2l0aCgnbGRhcHM6Ly8nKVxuICAgID8geyB1cmw6IG9wdGlvbnMudXJsLCB0bHNPcHRpb25zOiBvcHRpb25zLnRsc09wdGlvbnMgfVxuICAgIDogeyB1cmw6IG9wdGlvbnMudXJsIH07XG5cbiAgY29uc3QgY2xpZW50ID0gbGRhcGpzLmNyZWF0ZUNsaWVudChjbGllbnRPcHRpb25zKTtcbiAgY29uc3QgdXNlckNuID1cbiAgICB0eXBlb2Ygb3B0aW9ucy5kbiA9PT0gJ3N0cmluZydcbiAgICAgID8gb3B0aW9ucy5kbi5yZXBsYWNlKCd7e2lkfX0nLCBhdXRoRGF0YS5pZClcbiAgICAgIDogYHVpZD0ke2F1dGhEYXRhLmlkfSwke29wdGlvbnMuc3VmZml4fWA7XG5cbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBjbGllbnQuYmluZCh1c2VyQ24sIGF1dGhEYXRhLnBhc3N3b3JkLCBsZGFwRXJyb3IgPT4ge1xuICAgICAgZGVsZXRlIGF1dGhEYXRhLnBhc3N3b3JkO1xuICAgICAgaWYgKGxkYXBFcnJvcikge1xuICAgICAgICBsZXQgZXJyb3I7XG4gICAgICAgIHN3aXRjaCAobGRhcEVycm9yLmNvZGUpIHtcbiAgICAgICAgICBjYXNlIDQ5OlxuICAgICAgICAgICAgZXJyb3IgPSBuZXcgUGFyc2UuRXJyb3IoXG4gICAgICAgICAgICAgIFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsXG4gICAgICAgICAgICAgICdMREFQOiBXcm9uZyB1c2VybmFtZSBvciBwYXNzd29yZCdcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdERVBUSF9aRVJPX1NFTEZfU0lHTkVEX0NFUlQnOlxuICAgICAgICAgICAgZXJyb3IgPSBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCwgJ0xEQVBTOiBDZXJ0aWZpY2F0ZSBtaXNtYXRjaCcpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIGVycm9yID0gbmV3IFBhcnNlLkVycm9yKFxuICAgICAgICAgICAgICBQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELFxuICAgICAgICAgICAgICAnTERBUDogU29tdGhpbmcgd2VudCB3cm9uZyAoJyArIGxkYXBFcnJvci5jb2RlICsgJyknXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHJlamVjdChlcnJvcik7XG4gICAgICAgIGNsaWVudC5kZXN0cm95KGxkYXBFcnJvcik7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgaWYgKHR5cGVvZiBvcHRpb25zLmdyb3VwQ24gPT09ICdzdHJpbmcnICYmIHR5cGVvZiBvcHRpb25zLmdyb3VwRmlsdGVyID09PSAnc3RyaW5nJykge1xuICAgICAgICBzZWFyY2hGb3JHcm91cChjbGllbnQsIG9wdGlvbnMsIGF1dGhEYXRhLmlkLCByZXNvbHZlLCByZWplY3QpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY2xpZW50LnVuYmluZCgpO1xuICAgICAgICBjbGllbnQuZGVzdHJveSgpO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH0pO1xufVxuXG5mdW5jdGlvbiBvcHRpb25zQXJlVmFsaWQob3B0aW9ucykge1xuICByZXR1cm4gKFxuICAgIHR5cGVvZiBvcHRpb25zID09PSAnb2JqZWN0JyAmJlxuICAgIHR5cGVvZiBvcHRpb25zLnN1ZmZpeCA9PT0gJ3N0cmluZycgJiZcbiAgICB0eXBlb2Ygb3B0aW9ucy51cmwgPT09ICdzdHJpbmcnICYmXG4gICAgKG9wdGlvbnMudXJsLnN0YXJ0c1dpdGgoJ2xkYXA6Ly8nKSB8fFxuICAgICAgKG9wdGlvbnMudXJsLnN0YXJ0c1dpdGgoJ2xkYXBzOi8vJykgJiYgdHlwZW9mIG9wdGlvbnMudGxzT3B0aW9ucyA9PT0gJ29iamVjdCcpKVxuICApO1xufVxuXG5mdW5jdGlvbiBzZWFyY2hGb3JHcm91cChjbGllbnQsIG9wdGlvbnMsIGlkLCByZXNvbHZlLCByZWplY3QpIHtcbiAgY29uc3QgZmlsdGVyID0gb3B0aW9ucy5ncm91cEZpbHRlci5yZXBsYWNlKC97e2lkfX0vZ2ksIGlkKTtcbiAgY29uc3Qgb3B0cyA9IHtcbiAgICBzY29wZTogJ3N1YicsXG4gICAgZmlsdGVyOiBmaWx0ZXIsXG4gIH07XG4gIGxldCBmb3VuZCA9IGZhbHNlO1xuICBjbGllbnQuc2VhcmNoKG9wdGlvbnMuc3VmZml4LCBvcHRzLCAoc2VhcmNoRXJyb3IsIHJlcykgPT4ge1xuICAgIGlmIChzZWFyY2hFcnJvcikge1xuICAgICAgY2xpZW50LnVuYmluZCgpO1xuICAgICAgY2xpZW50LmRlc3Ryb3koKTtcbiAgICAgIHJldHVybiByZWplY3QobmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLklOVEVSTkFMX1NFUlZFUl9FUlJPUiwgJ0xEQVAgZ3JvdXAgc2VhcmNoIGZhaWxlZCcpKTtcbiAgICB9XG4gICAgcmVzLm9uKCdzZWFyY2hFbnRyeScsIGVudHJ5ID0+IHtcbiAgICAgIGlmIChlbnRyeS5wb2pvLmF0dHJpYnV0ZXMuZmluZChvYmogPT4gb2JqLnR5cGUgPT09ICdjbicpLnZhbHVlcy5pbmNsdWRlcyhvcHRpb25zLmdyb3VwQ24pKSB7XG4gICAgICAgIGZvdW5kID0gdHJ1ZTtcbiAgICAgICAgY2xpZW50LnVuYmluZCgpO1xuICAgICAgICBjbGllbnQuZGVzdHJveSgpO1xuICAgICAgICByZXR1cm4gcmVzb2x2ZSgpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHJlcy5vbignZW5kJywgKCkgPT4ge1xuICAgICAgaWYgKCFmb3VuZCkge1xuICAgICAgICBjbGllbnQudW5iaW5kKCk7XG4gICAgICAgIGNsaWVudC5kZXN0cm95KCk7XG4gICAgICAgIHJldHVybiByZWplY3QoXG4gICAgICAgICAgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLklOVEVSTkFMX1NFUlZFUl9FUlJPUiwgJ0xEQVA6IFVzZXIgbm90IGluIGdyb3VwJylcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICByZXMub24oJ2Vycm9yJywgKCkgPT4ge1xuICAgICAgY2xpZW50LnVuYmluZCgpO1xuICAgICAgY2xpZW50LmRlc3Ryb3koKTtcbiAgICAgIHJldHVybiByZWplY3QobmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLklOVEVSTkFMX1NFUlZFUl9FUlJPUiwgJ0xEQVAgZ3JvdXAgc2VhcmNoIGZhaWxlZCcpKTtcbiAgICB9KTtcbiAgfSk7XG59XG5cbmZ1bmN0aW9uIHZhbGlkYXRlQXBwSWQoKSB7XG4gIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIHZhbGlkYXRlQXBwSWQsXG4gIHZhbGlkYXRlQXV0aERhdGEsXG59O1xuIl0sIm1hcHBpbmdzIjoiOztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUdBLE1BQU1BLE1BQU0sR0FBR0MsT0FBTyxDQUFDLFFBQVEsQ0FBQztBQUNoQyxNQUFNQyxLQUFLLEdBQUdELE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQ0MsS0FBSztBQUV6QyxTQUFTQyxnQkFBZ0JBLENBQUNDLFFBQVEsRUFBRUMsT0FBTyxFQUFFO0VBQzNDLElBQUksQ0FBQ0MsZUFBZSxDQUFDRCxPQUFPLENBQUMsRUFBRTtJQUM3QixPQUFPLElBQUlFLE9BQU8sQ0FBQyxDQUFD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
|