From fd5136e09ce0fe34fb53d15cf9a1efbe5f880f4f Mon Sep 17 00:00:00 2001 From: Ryan Slatten Date: Fri, 20 Sep 2024 20:28:36 -0400 Subject: [PATCH 1/5] test --- plugins/arcgis/service/package-lock.json | 182 +++++++++++++++++- plugins/arcgis/service/package.json | 1 + plugins/arcgis/service/src/index.ts | 25 ++- .../src/lib/arc-admin/arc-admin.component.ts | 3 + .../projects/main/src/lib/arc.service.ts | 4 + 5 files changed, 211 insertions(+), 4 deletions(-) diff --git a/plugins/arcgis/service/package-lock.json b/plugins/arcgis/service/package-lock.json index 8fc472a33..4d7475055 100644 --- a/plugins/arcgis/service/package-lock.json +++ b/plugins/arcgis/service/package-lock.json @@ -9,7 +9,9 @@ "version": "1.0.0", "license": "Apache-2.0", "dependencies": { + "@esri/arcgis-rest-request": "^4.2.3", "@terraformer/arcgis": "2.1.2", + "cors": "^2.8.5", "form-data": "^4.0.0" }, "devDependencies": { @@ -635,6 +637,37 @@ "node": ">=0.1.95" } }, + "node_modules/@esri/arcgis-rest-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@esri/arcgis-rest-fetch/-/arcgis-rest-fetch-4.0.0.tgz", + "integrity": "sha512-ybsMO2L4cxx0IaIx0jv6/VbXidZmQIiGD3bvPF1/n1Y1ljHIhCvX+ti54cQSfg/HW2+VAVVnt8EPD/omVhNAyg==", + "dependencies": { + "node-fetch": "^3.0.0" + } + }, + "node_modules/@esri/arcgis-rest-form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@esri/arcgis-rest-form-data/-/arcgis-rest-form-data-4.0.0.tgz", + "integrity": "sha512-cAS9HONIJgseCDdgRCIHBR4CE/OQXZIRP3FoNx/w+XyjVqK6yQiwCeXpYLfHeEZ6GEQqrA9XUBxifWG9UaJyXA==", + "dependencies": { + "formdata-node": "^4.1.0" + } + }, + "node_modules/@esri/arcgis-rest-request": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@esri/arcgis-rest-request/-/arcgis-rest-request-4.2.3.tgz", + "integrity": "sha512-5jpgZIN9v8L4q1HRFG1rvn10ay/8UBskvjFMfmvGX+0AsZVVo339ENW1VIwZUoYKv7s06FRWGOcO6EYHq+e+6A==", + "license": "Apache-2.0", + "dependencies": { + "@esri/arcgis-rest-fetch": "^4.0.0", + "@esri/arcgis-rest-form-data": "^4.0.0", + "mitt": "^3.0.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -7367,8 +7400,6 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "extraneous": true, - "optional": true, - "dev": true, "hasInstallScript": true, "os": [ "darwin" @@ -15929,6 +15960,19 @@ "node": ">=0.10.0" } }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cp-file": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-7.0.0.tgz", @@ -16024,6 +16068,15 @@ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", "dev": true }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -16802,6 +16855,38 @@ "bser": "2.1.1" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fetch-blob/node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -16900,6 +16985,31 @@ "node": ">= 6" } }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -19433,6 +19543,12 @@ "node": ">=0.10.0" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, "node_modules/mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -19550,6 +19666,43 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -19801,6 +19954,15 @@ "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==", "dev": true }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -21765,6 +21927,12 @@ "node": ">=10" } }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -22123,7 +22291,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "peer": true, "engines": { "node": ">= 0.8" } @@ -22159,6 +22326,15 @@ "makeerror": "1.0.12" } }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", diff --git a/plugins/arcgis/service/package.json b/plugins/arcgis/service/package.json index e2139ba1d..6ec3a718b 100644 --- a/plugins/arcgis/service/package.json +++ b/plugins/arcgis/service/package.json @@ -38,6 +38,7 @@ }, "homepage": "https://github.com/ngageoint/mage-server#readme", "dependencies": { + "@esri/arcgis-rest-request": "^4.2.3", "@terraformer/arcgis": "2.1.2", "form-data": "^4.0.0" }, diff --git a/plugins/arcgis/service/src/index.ts b/plugins/arcgis/service/src/index.ts index f888cabf3..bf7b8d222 100644 --- a/plugins/arcgis/service/src/index.ts +++ b/plugins/arcgis/service/src/index.ts @@ -7,8 +7,15 @@ import { SettingPermission } from '@ngageoint/mage.service/lib/entities/authoriz import express from 'express' import { ArcGISPluginConfig } from './ArcGISPluginConfig' import { ObservationProcessor } from './ObservationProcessor' -import {HttpClient} from './HttpClient' +import { HttpClient } from './HttpClient' import { FeatureServiceResult } from './FeatureServiceResult' +import { ArcGISIdentityManager } from "@esri/arcgis-rest-request" + +const credentials = { + clientId: 'dzoVuv7Apb5gjJIP', + portal: "https://arcgis.geointnext.com/arcgis/sharing/rest", + redirectUri: 'http://localhost:4242/plugins/@ngageoint/mage.arcgis.service/authenticate' +} const logPrefix = '[mage.arcgis]' const logMethods = ['log', 'debug', 'info', 'warn', 'error'] as const @@ -75,6 +82,22 @@ const arcgisPluginHooks: InitPluginHook = { processor.putConfig(arcConfig) res.status(200).json({}) }) + routes.route('/sign-in') + .get(async (req, res, next) => { + ArcGISIdentityManager.authorize(credentials, res); + }) + routes.route('/authenticate') + .get(async (req, res, next) => { + const code = req.query.code as string; + ArcGISIdentityManager.exchangeAuthorizationCode(credentials, code) + .then((identityManager: ArcGISIdentityManager) => { + identityManager.getUsername().then((username: string) => { + console.info('logged in user', username) + }) + }).catch((error) => { + console.error(error) + }); + }) routes.route('/arcgisLayers') .get(async (req, res, next) => { const featureUrl = req.query.featureUrl as string; diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts index 914ba8304..1aadf6de0 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts @@ -61,6 +61,9 @@ export class ArcAdminComponent implements OnInit { this.config = x; arcService.fetchPopulatedEvents().subscribe(x => this.handleEventResults(x)); }) + arcService.authenticate().subscribe(x => { + console.log(x) + }) } configChanged(config: ArcGISPluginConfig) { diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts index 7f1b1a41b..ac0a19026 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts @@ -30,6 +30,10 @@ export class ArcService { return this.http.get(`${baseUrl}/arcgisLayers?featureUrl=${featureUrl}`) } + authenticate() { + return this.http.get(`${baseUrl}/sign-in`) + } + fetchEvents() { return this.http.get(`${apiBaseUrl}/events?populate=false&projection={"name":true,"id":true}`) } From 760f8b468f3a1f030a89cd0d0c862f8736e817c0 Mon Sep 17 00:00:00 2001 From: Ryan Slatten Date: Wed, 25 Sep 2024 15:20:01 -0400 Subject: [PATCH 2/5] store --- plugins/arcgis/service/src/index.ts | 9 ++++--- .../src/lib/arc-admin/arc-admin.component.ts | 3 --- .../lib/arc-layer/arc-layer.component.html | 4 +++ .../src/lib/arc-layer/arc-layer.component.ts | 16 +++++++++++- .../projects/main/src/lib/arc.service.ts | 25 ++++++++++++++++--- service/src/app.ts | 4 +-- 6 files changed, 49 insertions(+), 12 deletions(-) diff --git a/plugins/arcgis/service/src/index.ts b/plugins/arcgis/service/src/index.ts index bf7b8d222..ae3972104 100644 --- a/plugins/arcgis/service/src/index.ts +++ b/plugins/arcgis/service/src/index.ts @@ -63,9 +63,9 @@ const arcgisPluginHooks: InitPluginHook = { .use(async (req, res, next) => { const context = requestContext(req) const user = context.requestingPrincipal() - if (!user.role.permissions.find(x => x === SettingPermission.UPDATE_SETTINGS)) { - return res.status(403).json({ message: 'unauthorized' }) - } + // if (!user.role?.permissions.find(x => x === SettingPermission.UPDATE_SETTINGS)) { + // return res.status(403).json({ message: 'unauthorized' }) + // } next() }) routes.route('/config') @@ -82,6 +82,7 @@ const arcgisPluginHooks: InitPluginHook = { processor.putConfig(arcConfig) res.status(200).json({}) }) + // Get ArcGIS login status routes.route('/sign-in') .get(async (req, res, next) => { ArcGISIdentityManager.authorize(credentials, res); @@ -93,9 +94,11 @@ const arcgisPluginHooks: InitPluginHook = { .then((identityManager: ArcGISIdentityManager) => { identityManager.getUsername().then((username: string) => { console.info('logged in user', username) + res.status(200).json({ username }) }) }).catch((error) => { console.error(error) + next(); }); }) routes.route('/arcgisLayers') diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts index 1aadf6de0..914ba8304 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-admin/arc-admin.component.ts @@ -61,9 +61,6 @@ export class ArcAdminComponent implements OnInit { this.config = x; arcService.fetchPopulatedEvents().subscribe(x => this.handleEventResults(x)); }) - arcService.authenticate().subscribe(x => { - console.log(x) - }) } configChanged(config: ArcGISPluginConfig) { diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.html b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.html index 72ae1129f..874633f82 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.html +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.html @@ -8,6 +8,10 @@

Feature Layers

+
+ +
There are no ArcGIS feature services currently being synchronized. diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts index 330f54faf..3a86a3483 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts @@ -24,7 +24,7 @@ export class ArcLayerComponent implements OnInit { isLoading: boolean; currentUrl?: string; private timeoutId: number; - + @ViewChild('addLayerDialog', { static: true }) private addLayerTemplate: TemplateRef @ViewChild('deleteLayerDialog', { static: true }) @@ -103,6 +103,20 @@ export class ArcLayerComponent implements OnInit { }) } + onSignIn() { + this.arcService.authenticate().subscribe({ + next(x) { + console.log('got value ' + JSON.stringify(x)); + }, + error(err) { + console.error('something wrong occurred: ' + err); + }, + complete() { + console.log('done'); + }, + }); + } + onAddLayer() { this.currentUrl = undefined this.arcLayerControl.setValue('') diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts index ac0a19026..1edb03965 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts @@ -1,6 +1,6 @@ import { HttpClient } from '@angular/common/http' import { Injectable } from '@angular/core' -import { Observable } from 'rxjs' +import { Observable, Subject } from 'rxjs' import { ArcGISPluginConfig } from './ArcGISPluginConfig' import { FeatureServiceResult } from './FeatureServiceResult' import { EventResult } from './EventsResult' @@ -30,8 +30,27 @@ export class ArcService { return this.http.get(`${baseUrl}/arcgisLayers?featureUrl=${featureUrl}`) } - authenticate() { - return this.http.get(`${baseUrl}/sign-in`) + authenticate(): Observable { + let subject = new Subject(); + + const url = `${baseUrl}/sign-in`; + const authWindow = window.open(url, "_blank"); + + function onMessage(event: any) { + window.removeEventListener('message', onMessage, false); + + if (event.origin !== window.location.origin) { + return; + } + + subject.next(event.data) + + authWindow?.close(); + } + + authWindow?.addEventListener('message', onMessage, false); + + return subject.asObservable() } fetchEvents() { diff --git a/service/src/app.ts b/service/src/app.ts index ed8255a87..2b214d709 100644 --- a/service/src/app.ts +++ b/service/src/app.ts @@ -628,7 +628,7 @@ async function initWebLayer(repos: Repositories, app: AppLayer, webUIPlugins: st middleware applies the entity form of a user on the request rather than the mongoose document instance */ - return { ...req.user.toJSON(), id: req.user._id.toHexString() } as UserExpanded + return { ...req.user?.toJSON(), id: req.user?._id.toHexString() } as UserExpanded }, locale() { return Object.freeze({ @@ -650,7 +650,7 @@ async function initWebLayer(repos: Repositories, app: AppLayer, webUIPlugins: st webController, addAuthenticatedPluginRoutes: (pluginId: string, initPluginRoutes: WebRoutesHooks['webRoutes']) => { const routes = initPluginRoutes(pluginAppRequestContext) - webController.use(`/plugins/${pluginId}`, [ bearerAuth, routes ]) + webController.use(`/plugins/${pluginId}`, [ routes ]) } } } From cbf1f18b1108bc5e0fe05575c9e60f21587bb91b Mon Sep 17 00:00:00 2001 From: Ryan Slatten Date: Thu, 26 Sep 2024 15:32:34 -0400 Subject: [PATCH 3/5] store --- plugins/arcgis/service/src/index.ts | 21 +++++++++++-------- .../projects/main/src/lib/arc.service.ts | 4 ++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/plugins/arcgis/service/src/index.ts b/plugins/arcgis/service/src/index.ts index fac3fdd50..3cf3193fe 100644 --- a/plugins/arcgis/service/src/index.ts +++ b/plugins/arcgis/service/src/index.ts @@ -11,10 +11,11 @@ import { HttpClient } from './HttpClient' import { FeatureServiceResult } from './FeatureServiceResult' import { ArcGISIdentityManager } from "@esri/arcgis-rest-request" -const credentials = { +// TODO: Move configuration to be supplied by the user instead of hardcoded +const oauthCreds = { clientId: 'dzoVuv7Apb5gjJIP', portal: "https://arcgis.geointnext.com/arcgis/sharing/rest", - redirectUri: 'http://localhost:4242/plugins/@ngageoint/mage.arcgis.service/authenticate' + redirectUri: 'http://localhost:4242/plugins/@ngageoint/mage.arcgis.service/oauth/authenticate' } const logPrefix = '[mage.arcgis]' @@ -59,15 +60,17 @@ const arcgisPluginHooks: InitPluginHook = { return { webRoutes: { public: (requestContext: GetAppRequestContext) => { - const routes = express.Router().use(express.json()) - routes.get('/oauth/signin', async (req, res, next) => { - ArcGISIdentityManager.authorize(credentials, res); - }) + const routes = express.Router().use(express.json()); + routes.get('/oauth/sign-in', async (req, res, next) => { + ArcGISIdentityManager.authorize(oauthCreds, res); + }); - routes.post('/oauth/authenticate', async (req, res, next) => { + routes.get('/oauth/authenticate', async (req, res, next) => { const code = req.query.code as string; - ArcGISIdentityManager.exchangeAuthorizationCode(credentials, code) + ArcGISIdentityManager.exchangeAuthorizationCode(oauthCreds, code) .then((identityManager: ArcGISIdentityManager) => { + + identityManager.getUsername().then((username: string) => { console.info('logged in user', username) res.status(200).json({ username }) @@ -76,7 +79,7 @@ const arcgisPluginHooks: InitPluginHook = { console.error(error) next(); }); - }) + }); return routes }, diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts index 1edb03965..f940c2738 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts @@ -33,7 +33,7 @@ export class ArcService { authenticate(): Observable { let subject = new Subject(); - const url = `${baseUrl}/sign-in`; + const url = `${baseUrl}/oauth/sign-in`; const authWindow = window.open(url, "_blank"); function onMessage(event: any) { @@ -45,7 +45,7 @@ export class ArcService { subject.next(event.data) - authWindow?.close(); + // authWindow?.close(); } authWindow?.addEventListener('message', onMessage, false); From c22d6778649cdc0975378fa42386784508e730c4 Mon Sep 17 00:00:00 2001 From: Ryan Slatten Date: Sat, 28 Sep 2024 23:21:41 -0400 Subject: [PATCH 4/5] add clientid and secret --- plugins/arcgis/service/src/ArcGISConfig.ts | 14 +++- plugins/arcgis/service/src/HttpClient.ts | 5 +- plugins/arcgis/service/src/index.ts | 72 +++++++++---------- .../projects/main/src/lib/ArcGISConfig.ts | 18 ++++- .../lib/arc-layer/arc-layer.component.html | 2 +- .../src/lib/arc-layer/arc-layer.component.ts | 43 ++++++----- 6 files changed, 86 insertions(+), 68 deletions(-) diff --git a/plugins/arcgis/service/src/ArcGISConfig.ts b/plugins/arcgis/service/src/ArcGISConfig.ts index be99ed10b..82b69a769 100644 --- a/plugins/arcgis/service/src/ArcGISConfig.ts +++ b/plugins/arcgis/service/src/ArcGISConfig.ts @@ -85,12 +85,22 @@ export interface ArcGISAuthConfig { /** * The username for authentication. */ - username: string + username?: string /** * The password for authentication. */ - password: string + password?: string + + /** + * The Client Id for OAuth + */ + clientId?: string + + /** + * The Client secret for OAuth + */ + clientSecret?: string } /** diff --git a/plugins/arcgis/service/src/HttpClient.ts b/plugins/arcgis/service/src/HttpClient.ts index a68841f80..5c4497dd8 100644 --- a/plugins/arcgis/service/src/HttpClient.ts +++ b/plugins/arcgis/service/src/HttpClient.ts @@ -121,11 +121,14 @@ export class HttpClient { * Sends a get request to the specified url. * @param url The url of the get request. */ - sendGet(url: string) { + async sendGet(url: string): Promise { const console = this._console + let response; this.sendGetHandleResponse(url, function (chunk) { console.log('Response: ' + chunk); + response = chunk; }) + return response; } /** diff --git a/plugins/arcgis/service/src/index.ts b/plugins/arcgis/service/src/index.ts index 54b920f92..93582fd1c 100644 --- a/plugins/arcgis/service/src/index.ts +++ b/plugins/arcgis/service/src/index.ts @@ -12,13 +12,6 @@ import { FeatureServiceResult } from './FeatureServiceResult' import { ArcGISIdentityManager } from "@esri/arcgis-rest-request" // import { IQueryFeaturesOptions, queryFeatures } from '@esri/arcgis-rest-feature-service' -// TODO: Move configuration to be supplied by the user instead of hardcoded -const oauthCreds = { - clientId: 'dzoVuv7Apb5gjJIP', - portal: "https://arcgis.geointnext.com/arcgis/sharing/rest", - redirectUri: 'http://localhost:4242/plugins/@ngageoint/mage.arcgis.service/oauth/authenticate' -} - const logPrefix = '[mage.arcgis]' const logMethods = ['log', 'debug', 'info', 'warn', 'error'] as const const consoleOverrides = logMethods.reduce((overrides, fn) => { @@ -71,25 +64,44 @@ function getServerUrl(featureServiceUrl: string): string { * * @throws {Error} If the identity manager could not be created due to missing required query parameters. */ -async function handleAuthentication(req: express.Request): Promise { +async function handleAuthentication(req: express.Request, httpClient: HttpClient): Promise { const featureUsername = req.query.username as string | undefined; const featurePassword = req.query.password as string | undefined; + const featureClientId = req.query.clientId as string | undefined; + const featureClientSecret = req.query.clientSecret as string | undefined; const featureServer = req.query.server as string | undefined; const featurePortal = req.query.portal as string | undefined; const featureToken = req.query.token as string | undefined; + const portalUrl = getPortalUrl(req.query.featureUrl as string ?? ''); let identityManager: ArcGISIdentityManager; try { if (featureToken) { console.log('Token provided for authentication'); - identityManager = await ArcGISIdentityManager.fromToken({ token: featureToken, server: getServerUrl(req.query.featureUrl as string ?? ''), portal: getPortalUrl(req.query.featureUrl as string ?? '') }); + identityManager = await ArcGISIdentityManager.fromToken({ token: featureToken, server: getServerUrl(req.query.featureUrl as string ?? ''), portal: portalUrl }); } else if (featureUsername && featurePassword) { console.log('Username and password provided for authentication, username:' + featureUsername); identityManager = await ArcGISIdentityManager.signIn({ username: featureUsername, password: featurePassword, - portal: getPortalUrl(req.query.featureUrl as string ?? ''), + portal: portalUrl, + }); + } else if (featureClientId && featureClientSecret) { + console.log('ClientId and Client secret provided for authentication'); + const params = { + client_id: featureClientId, + client_secret: featureClientSecret, + grant_type: 'client_credentials', + expiration: 900 + } + + const url = `${portalUrl}/oauth2/token?client_id=${params.client_id}&client_secret=${oauthCreds.clientSecret}&grant_type=${params.grant_type}&expiration=${params.expiration}` + const response = await httpClient.sendGet(url); + identityManager = await ArcGISIdentityManager.fromToken({ + clientId: featureClientId, + token: JSON.parse(response)?.access_token || '', + portal: portalUrl }); } else { throw new Error('Missing required query parameters to authenticate (token or username/password).'); @@ -124,6 +136,7 @@ const arcgisPluginHooks: InitPluginHook = { // - Move getPortalUrl to Helper file // - Update layer token to get token from identity manager // - Move plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts addLayer to helper file and use instead of encodeURIComponent + // - Remove Client secret from returned Config object if applicable const processor = new ObservationProcessor(stateRepo, eventRepo, obsRepoForEvent, userRepo, console); processor.start(); @@ -131,26 +144,7 @@ const arcgisPluginHooks: InitPluginHook = { webRoutes: { public: (requestContext: GetAppRequestContext) => { const routes = express.Router().use(express.json()); - routes.get('/oauth/sign-in', async (req, res, next) => { - ArcGISIdentityManager.authorize(oauthCreds, res); - }); - - routes.get('/oauth/authenticate', async (req, res, next) => { - const code = req.query.code as string; - ArcGISIdentityManager.exchangeAuthorizationCode(oauthCreds, code) - .then((identityManager: ArcGISIdentityManager) => { - - - identityManager.getUsername().then((username: string) => { - console.info('logged in user', username) - res.status(200).json({ username }) - }) - }).catch((error) => { - console.error(error) - next(); - }); - }); - + // TODO: Add User initiated Oauth return routes }, protected: (requestContext: GetAppRequestContext) => { @@ -183,15 +177,15 @@ const arcgisPluginHooks: InitPluginHook = { const featureUrl = req.query.featureUrl as string; console.info('Getting ArcGIS layer info for ' + featureUrl) let identityManager: ArcGISIdentityManager; + const httpClient = new HttpClient(console); - try { - identityManager = await handleAuthentication(req); + try { + identityManager = await handleAuthentication(req, httpClient); - const featureUrlAndToken = featureUrl + '?token=' + encodeURIComponent(identityManager.token); - console.log('featureUrlAndToken', featureUrlAndToken); - const httpClient = new HttpClient(console); - - httpClient.sendGetHandleResponse(featureUrlAndToken, (chunk) => { + const featureUrlAndToken = featureUrl + '?token=' + encodeURIComponent(identityManager.token); + console.log('featureUrlAndToken', featureUrlAndToken); + + httpClient.sendGetHandleResponse(featureUrlAndToken, (chunk) => { console.info('ArcGIS layer info response ' + chunk); try { const featureServiceResult = JSON.parse(chunk) as FeatureServiceResult; @@ -206,8 +200,8 @@ const arcgisPluginHooks: InitPluginHook = { } }); } catch (err) { - res.status(500).json({ message: 'Could not get ArcGIS layer info', error: err }); - } + res.status(500).json({ message: 'Could not get ArcGIS layer info', error: err }); + } }) return routes diff --git a/plugins/arcgis/web-app/projects/main/src/lib/ArcGISConfig.ts b/plugins/arcgis/web-app/projects/main/src/lib/ArcGISConfig.ts index be99ed10b..15194f081 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/ArcGISConfig.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/ArcGISConfig.ts @@ -11,7 +11,7 @@ export interface FeatureServiceConfig { /** * Access token */ - token?: string + token?: string // TODO?: Perhaps move to the auth property? /** * Username and password for ArcGIS authentication @@ -82,15 +82,27 @@ export interface FeatureLayerConfig { */ export interface ArcGISAuthConfig { + // TODO?: May want to add authType property + /** * The username for authentication. */ - username: string + username?: string /** * The password for authentication. */ - password: string + password?: string + + /** + * The Client Id for OAuth + */ + clientId?: string + + /** + * The Client secret for OAuth + */ + clientSecret?: string } /** diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.html b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.html index d3abf791e..c937d7e3e 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.html +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.html @@ -74,7 +74,7 @@

Layers

+ (click)="onAddLayerUrl({ layerUrl: layerUrl.value, selectableLayers: layers, layerToken: layerToken.value })">SAVE diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts index 02138d7e5..f523458f9 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc-layer/arc-layer.component.ts @@ -165,12 +165,6 @@ export class ArcLayerComponent implements OnInit { this.arcService.putArcConfig(this.config); } - - // Define the overloads - onAddLayerUrl(layerUrl: string, layerToken: string, layers: ArcLayerSelectable[]): void; - onAddLayerUrl(layerUrl: string, username: string, password: string, layers: ArcLayerSelectable[]): void; - - // Implement the function /** * Adds a new layer to the configuration if it does not already exist. * @@ -188,8 +182,17 @@ export class ArcLayerComponent implements OnInit { * 6. Updates the configuration and emits the change. * 7. Persists the updated configuration using `arcService`. */ - onAddLayerUrl(layerUrl: string, arg2: string, arg3: string | ArcLayerSelectable[], arg4?: ArcLayerSelectable[]): void { + onAddLayerUrl(params: { + layerUrl: string, + selectableLayers: ArcLayerSelectable[], + layerToken?: string, + username?: string, + password?: string, + clientId?: string, + clientSecret?: string + }): void { let serviceConfigToEdit = null; + const { layerUrl, selectableLayers, layerToken, username, password, clientId, clientSecret } = params; // Search if the layer in config to edit for (const service of this.config.featureServices) { @@ -197,34 +200,30 @@ export class ArcLayerComponent implements OnInit { serviceConfigToEdit = service; } } - // Determine if layers in 3rd or 4th argument - const layers = typeof arg3 === 'string' ? arg4 : arg3; // Add layer if it doesn't exist if (serviceConfigToEdit == null) { console.log('Adding layer ' + layerUrl); - let token: string | null = null; const featureLayer: FeatureServiceConfig = { url: layerUrl, token: undefined, - auth: { - username: '', - password: '' - }, + auth: {}, layers: [] } as FeatureServiceConfig; - if (typeof arg3 === 'string') { + if (username) { // Handle username and password case - featureLayer.auth = { username: arg2, password: arg3 }; - } else { + featureLayer.auth = { username, password }; + } else if (clientId) { + featureLayer.auth = { clientId, clientSecret }; + }else { // Handle token case - featureLayer.token = arg2; + featureLayer.token = layerToken; } - if (layers) { - for (const aLayer of layers) { + if (selectableLayers) { + for (const aLayer of selectableLayers) { if (aLayer.isSelected) { const layerConfig = { layer: aLayer.name, @@ -243,8 +242,8 @@ export class ArcLayerComponent implements OnInit { } else { // Edit existing layer console.log('Saving edited layer ' + layerUrl) const editedLayers = []; - if (layers) { - for (const aLayer of layers) { + if (selectableLayers) { + for (const aLayer of selectableLayers) { if (aLayer.isSelected) { let layerConfig = null if (serviceConfigToEdit.layers != null) { From c209fbea9e4859295fe9d76341498ece1824a7e7 Mon Sep 17 00:00:00 2001 From: Ryan Slatten Date: Sat, 28 Sep 2024 23:30:47 -0400 Subject: [PATCH 5/5] fix build --- plugins/arcgis/service/package-lock.json | 24 +----------------------- plugins/arcgis/service/src/index.ts | 2 +- service/src/app.ts | 2 +- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/plugins/arcgis/service/package-lock.json b/plugins/arcgis/service/package-lock.json index 73b86b16d..246d648d1 100644 --- a/plugins/arcgis/service/package-lock.json +++ b/plugins/arcgis/service/package-lock.json @@ -12,7 +12,6 @@ "@esri/arcgis-rest-feature-service": "^4.0.6", "@esri/arcgis-rest-request": "^4.2.3", "@terraformer/arcgis": "2.1.2", - "cors": "^2.8.5", "form-data": "^4.0.0" }, "devDependencies": { @@ -15993,19 +15992,6 @@ "node": ">=0.10.0" } }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/cp-file": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-7.0.0.tgz", @@ -19987,15 +19973,6 @@ "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==", "dev": true }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -22324,6 +22301,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "peer": true, "engines": { "node": ">= 0.8" } diff --git a/plugins/arcgis/service/src/index.ts b/plugins/arcgis/service/src/index.ts index 93582fd1c..66902d6cb 100644 --- a/plugins/arcgis/service/src/index.ts +++ b/plugins/arcgis/service/src/index.ts @@ -96,7 +96,7 @@ async function handleAuthentication(req: express.Request, httpClient: HttpClient expiration: 900 } - const url = `${portalUrl}/oauth2/token?client_id=${params.client_id}&client_secret=${oauthCreds.clientSecret}&grant_type=${params.grant_type}&expiration=${params.expiration}` + const url = `${portalUrl}/oauth2/token?client_id=${params.client_id}&client_secret=${params.client_secret}&grant_type=${params.grant_type}&expiration=${params.expiration}` const response = await httpClient.sendGet(url); identityManager = await ArcGISIdentityManager.fromToken({ clientId: featureClientId, diff --git a/service/src/app.ts b/service/src/app.ts index 8da84f81a..85c4dbd1c 100644 --- a/service/src/app.ts +++ b/service/src/app.ts @@ -631,7 +631,7 @@ async function initWebLayer( middleware applies the entity form of a user on the request rather than the mongoose document instance */ - return { ...req.user?.toJSON(), id: req.user?._id.toHexString() } as UserExpanded + return { ...req.user.toJSON(), id: req.user._id.toHexString() } as UserExpanded }, locale() { return Object.freeze({