From cb0da1748e4bfe8239b8dc621aa1a460d717c462 Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:11:52 -0500 Subject: [PATCH 01/13] WIP - auth on systeminfo --- .../adapters.systemInfo.controllers.web.ts | 16 ++++++- .../app.api/systemInfo/app.api.systemInfo.ts | 18 ++++++-- .../systemInfo/app.impl.systemInfo.ts | 24 ++++++++++- service/src/app.ts | 11 +++-- .../authorization/entities.permissions.ts | 6 +++ .../src/permissions/permissions.systemInfo.ts | 31 +++++++++++++ ...dapters.systemInfo.controllers.web.test.ts | 20 ++++++++- .../app/systemInfo/app.systemInfo.test.ts | 43 +++++++++++++++---- 8 files changed, 148 insertions(+), 21 deletions(-) create mode 100644 service/src/permissions/permissions.systemInfo.ts diff --git a/service/src/adapters/systemInfo/adapters.systemInfo.controllers.web.ts b/service/src/adapters/systemInfo/adapters.systemInfo.controllers.web.ts index c2a4006ca..7b0cc995d 100644 --- a/service/src/adapters/systemInfo/adapters.systemInfo.controllers.web.ts +++ b/service/src/adapters/systemInfo/adapters.systemInfo.controllers.web.ts @@ -2,6 +2,10 @@ import express from 'express' import { WebAppRequestFactory } from '../adapters.controllers.web' import { SystemInfoAppLayer } from '../../app.api/systemInfo/app.api.systemInfo' +import { AppRequest, AppRequestContext } from '../../app.api/app.api.global' +import { UserWithRole } from '../../permissions/permissions.role-based.base' + +type systemInfoRequestType = AppRequest>; export function SystemInfoRoutes(appLayer: SystemInfoAppLayer, createAppRequest: WebAppRequestFactory): express.Router { @@ -10,8 +14,16 @@ export function SystemInfoRoutes(appLayer: SystemInfoAppLayer, createAppRequest: routes.route('/') .get(async (req, res, next) => { - const appReq = createAppRequest(req) - + const appReq = createAppRequest(req) + + const permissionError = await appLayer.permissionsService.ensureReadSystemInfoPermission( + appReq.context + ); + + if (permissionError) { + return next(permissionError); + } + const appRes = await appLayer.readSystemInfo(appReq) if (appRes.success) { return res.json(appRes.success) diff --git a/service/src/app.api/systemInfo/app.api.systemInfo.ts b/service/src/app.api/systemInfo/app.api.systemInfo.ts index e141d80cb..016558df7 100644 --- a/service/src/app.api/systemInfo/app.api.systemInfo.ts +++ b/service/src/app.api/systemInfo/app.api.systemInfo.ts @@ -1,13 +1,16 @@ import { SystemInfo } from '../../entities/systemInfo/entities.systemInfo' -import { InfrastructureError } from '../app.api.errors' -import { AppRequest, AppResponse } from '../app.api.global' +import { UserWithRole } from '../../permissions/permissions.role-based.base' +import { InfrastructureError, PermissionDeniedError } from '../app.api.errors' +import { AppRequest, AppRequestContext, AppResponse } from '../app.api.global' export type ExoPrivilegedSystemInfo = SystemInfo export type ExoRedactedSystemInfo = Omit export type ExoSystemInfo = ExoPrivilegedSystemInfo | ExoRedactedSystemInfo -export interface ReadSystemInfoRequest extends AppRequest {} +export interface ReadSystemInfoRequest extends AppRequest { + context: AppRequestContext; +} export interface ReadSystemInfoResponse extends AppResponse {} export interface ReadSystemInfo { @@ -15,5 +18,12 @@ export interface ReadSystemInfo { } export interface SystemInfoAppLayer { - readSystemInfo: ReadSystemInfo + readSystemInfo: ReadSystemInfo; + permissionsService: SystemInfoPermissionService; +} + +export interface SystemInfoPermissionService { + ensureReadSystemInfoPermission( + context: AppRequestContext + ): Promise; } \ No newline at end of file diff --git a/service/src/app.impl/systemInfo/app.impl.systemInfo.ts b/service/src/app.impl/systemInfo/app.impl.systemInfo.ts index be7da0101..fae4a9120 100644 --- a/service/src/app.impl/systemInfo/app.impl.systemInfo.ts +++ b/service/src/app.impl/systemInfo/app.impl.systemInfo.ts @@ -4,6 +4,9 @@ import { EnvironmentService } from '../../entities/systemInfo/entities.systemInf import * as Settings from '../../models/setting'; import * as AuthenticationConfiguration from '../../models/authenticationconfiguration'; import AuthenticationConfigurationTransformer from '../../transformers/authenticationconfiguration'; +import { RoleBasedSystemInfoPermissionService } from '../../permissions/permissions.systemInfo'; +import { permissionDenied } from '../../app.api/app.api.errors'; +import { SystemInfoPermission } from '../../entities/authorization/entities.permissions'; /** * This factory function creates the implementation of the {@link api.ReadSystemInfo} * application layer interface. @@ -13,7 +16,8 @@ export function CreateReadSystemInfo( config: any, settingsModule: typeof Settings = Settings, authConfigModule: typeof AuthenticationConfiguration = AuthenticationConfiguration, - authConfigTransformerModule: typeof AuthenticationConfigurationTransformer = AuthenticationConfigurationTransformer + authConfigTransformerModule: typeof AuthenticationConfigurationTransformer = AuthenticationConfigurationTransformer, + permissions: RoleBasedSystemInfoPermissionService ): api.ReadSystemInfo { // appending the authentication strategies to the api @@ -45,6 +49,24 @@ export function CreateReadSystemInfo( return async function readSystemInfo( req: api.ReadSystemInfoRequest ): Promise { + + const permissionError = await permissions.ensureReadSystemInfoPermission( + req.context + ); + + if (permissionError) { + throw permissionError; + } + + const user = req.context.requestingPrincipal(); + if (user && user.username !== 'admin') { + throw permissionDenied( + SystemInfoPermission.READ_SYSTEM_INFO, + user.username, + 'SystemInfo' + ); + } + // TODO: will need a permission check to determine what level of system // information the requesting principal is allowed to see const environment = await environmentService.readEnvironmentInfo(); diff --git a/service/src/app.ts b/service/src/app.ts index e97735854..759505948 100644 --- a/service/src/app.ts +++ b/service/src/app.ts @@ -65,6 +65,7 @@ import { CreateReadSystemInfo } from './app.impl/systemInfo/app.impl.systemInfo' import AuthenticationConfiguration from "./models/authenticationconfiguration"; import AuthenticationConfigurationTransformer from "./transformers/authenticationconfiguration"; import { SystemInfoRoutes } from './adapters/systemInfo/adapters.systemInfo.controllers.web' +import { RoleBasedSystemInfoPermissionService } from './permissions/permissions.systemInfo' export interface MageService { @@ -478,15 +479,18 @@ function initFeedsAppLayer(repos: Repositories): AppLayer['feeds'] { } function initSystemInfoAppLayer(repos: Repositories): SystemInfoAppLayer { + const permissionsService = new RoleBasedSystemInfoPermissionService(); return { readSystemInfo: CreateReadSystemInfo( repos.enviromentInfo, apiConfig, Settings, AuthenticationConfiguration, - AuthenticationConfigurationTransformer - ) - } + AuthenticationConfigurationTransformer, + permissionsService + ), + permissionsService + }; } interface MageEventRequestContext extends AppRequestContext { @@ -531,6 +535,7 @@ async function initWebLayer(repos: Repositories, app: AppLayer, webUIPlugins: st ]) const systemInfoRoutes = SystemInfoRoutes(app.systemInfo, appRequestFactory) webController.use('/api', [ + bearerAuth, systemInfoRoutes ]) const observationRequestFactory: ObservationWebAppRequestFactory = (req: express.Request, params: Params) => { diff --git a/service/src/entities/authorization/entities.permissions.ts b/service/src/entities/authorization/entities.permissions.ts index 69214dc29..d73302926 100644 --- a/service/src/entities/authorization/entities.permissions.ts +++ b/service/src/entities/authorization/entities.permissions.ts @@ -75,6 +75,11 @@ export enum SettingPermission { UPDATE_SETTINGS = 'UPDATE_SETTINGS' } + +export enum SystemInfoPermission { + READ_SYSTEM_INFO = 'READ_SYSTEM_INFO', +} + export enum FeedsPermission { FEEDS_LIST_SERVICE_TYPES = 'FEEDS_LIST_SERVICE_TYPES', FEEDS_CREATE_SERVICE = 'FEEDS_CREATE_SERVICE', @@ -115,6 +120,7 @@ export type AnyPermission = | SettingPermission | FeedsPermission | StaticIconPermission + | SystemInfoPermission const allPermissionsList = Object.freeze(Object.values(allPermissions)) diff --git a/service/src/permissions/permissions.systemInfo.ts b/service/src/permissions/permissions.systemInfo.ts new file mode 100644 index 000000000..f362cc8f0 --- /dev/null +++ b/service/src/permissions/permissions.systemInfo.ts @@ -0,0 +1,31 @@ +import { + permissionDenied, + PermissionDeniedError +} from '../app.api/app.api.errors'; +import { AppRequestContext } from '../app.api/app.api.global'; +import { SystemInfoPermissionService } from '../app.api/systemInfo/app.api.systemInfo'; +import { SystemInfoPermission } from '../entities/authorization/entities.permissions'; +import { + UserWithRole, + ensureContextUserHasPermission +} from './permissions.role-based.base'; + +export class RoleBasedSystemInfoPermissionService implements SystemInfoPermissionService { + async ensureReadSystemInfoPermission( ctx: AppRequestContext): Promise { + const user = ctx.requestingPrincipal(); + + // Check if the user is the "admin" + if (user && user.username !== 'admin') { + return permissionDenied( + SystemInfoPermission.READ_SYSTEM_INFO, + user.username, + 'SystemInfo' + ); + } + + return ensureContextUserHasPermission( + ctx, + SystemInfoPermission.READ_SYSTEM_INFO + ); + } +} diff --git a/service/test/adapters/systemInfo/adapters.systemInfo.controllers.web.test.ts b/service/test/adapters/systemInfo/adapters.systemInfo.controllers.web.test.ts index 56d19fbcf..1806ac319 100644 --- a/service/test/adapters/systemInfo/adapters.systemInfo.controllers.web.test.ts +++ b/service/test/adapters/systemInfo/adapters.systemInfo.controllers.web.test.ts @@ -6,7 +6,10 @@ import { WebAppRequestFactory } from '../../../lib/adapters/adapters.controllers import { Substitute as Sub, SubstituteOf, Arg } from '@fluffy-spoon/substitute'; import supertest from 'supertest'; import { SystemInfo } from '../../../lib/entities/systemInfo/entities.systemInfo'; -import { SystemInfoAppLayer } from '../../../lib/app.api/systemInfo/app.api.systemInfo'; +import { + SystemInfoAppLayer, + SystemInfoPermissionService +} from '../../../lib/app.api/systemInfo/app.api.systemInfo'; import { SystemInfoRoutes } from '../../../lib/adapters/systemInfo/adapters.systemInfo.controllers.web'; describe('SystemInfo web controller', () => { @@ -17,12 +20,25 @@ describe('SystemInfo web controller', () => { }; let client: supertest.SuperTest; - let appLayer: SubstituteOf; + + // Define and set up the mockPermissionsService + const mockPermissionsService = Sub.for(); + mockPermissionsService + .ensureReadSystemInfoPermission(Arg.any()) + .returns(Promise.resolve(null)); + + let appLayer: SubstituteOf = Sub.for< + SystemInfoAppLayer + >(); + + let appReqFactory: SubstituteOf; beforeEach(function() { + // Reset appLayer for every test. appLayer = Sub.for(); appReqFactory = Sub.for(); + const endpoint = express(); endpoint.use(function lookupUser( req: express.Request, diff --git a/service/test/app/systemInfo/app.systemInfo.test.ts b/service/test/app/systemInfo/app.systemInfo.test.ts index d6b20bdeb..49d344192 100644 --- a/service/test/app/systemInfo/app.systemInfo.test.ts +++ b/service/test/app/systemInfo/app.systemInfo.test.ts @@ -12,6 +12,9 @@ import { import * as Settings from '../../../lib/models/setting'; import * as AuthenticationConfiguration from '../../../lib/models/authenticationconfiguration'; import * as AuthenticationConfigurationTransformer from '../../../lib/transformers/authenticationconfiguration'; +import { UserWithRole } from '../../../lib/permissions/permissions.role-based.base'; +import { ReadSystemInfo, ReadSystemInfoRequest } from '../../../lib/app.api/systemInfo/app.api.systemInfo'; +import { RoleBasedSystemInfoPermissionService } from '../../../lib/permissions/permissions.systemInfo'; const mockNodeVersion = '14.16.1'; const mockMongoDBVersion = '4.2.0'; @@ -23,10 +26,26 @@ const mockEnvironmentInfo: EnvironmentInfo = { const mockDisclaimer = {}; const mockContactInfo = {}; +const mockUserWithRole = ({ + _id: 'mockObjectId', + id: 'testUserId', + username: 'testUser', + displayName: 'Test User', + phones: [], + active: true, + enabled: true, + roleId: 'mockRoleId', + authenticationId: 'mockAuthId', + recentEventIds: [], + createdAt: new Date(), + lastUpdated: new Date() +} as unknown) as UserWithRole; + + function requestBy( - principal: string, + principal: UserWithRole, // assuming you have some way to produce or mock a UserWithRole params?: T -): AppRequest & T { +): ReadSystemInfoRequest & T { if (!params) { params = {} as T; } @@ -42,10 +61,10 @@ function requestBy( }; } + + describe('CreateReadSystemInfo', () => { - let readSystemInfo: ( - arg0: AppRequest> & object - ) => any; + let readSystemInfo: ReadSystemInfo let mockedSettingsModule = Substitute.for(); let mockedAuthConfigModule = Substitute.for< @@ -64,8 +83,13 @@ describe('CreateReadSystemInfo', () => { } }; + const mockedPermissionsModule = new RoleBasedSystemInfoPermissionService(); + // mockedPermissionsModule + // .checkPermission(Arg.any()) + // .returns(Promise.resolve(true)); + + beforeEach(() => { - // Reinstantiate mock objects mockedSettingsModule = Substitute.for(); mockedAuthConfigModule = Substitute.for< typeof AuthenticationConfiguration @@ -91,12 +115,13 @@ describe('CreateReadSystemInfo', () => { mockConfig, mockedSettingsModule, mockedAuthConfigModule, - mockedAuthConfigTransformerModule + mockedAuthConfigTransformerModule, + mockedPermissionsModule ); }); it('should return a function that produces a ReadSystemInfoResponse with full SystemInfo', async () => { - const request = requestBy('test-principal'); + const request = requestBy(mockUserWithRole); const response = await readSystemInfo(request); expect(response).to.exist; @@ -116,7 +141,7 @@ describe('CreateReadSystemInfo', () => { }); it("should format the node version as 'major.minor.patch'", async () => { - const request = requestBy('test-principal'); + const request = requestBy(mockUserWithRole); const response = await readSystemInfo(request); const nodeVersion = (response.success as SystemInfo).environment .nodeVersion; From a08d393a08777358d53207b0943b1e624ac120b1 Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Wed, 30 Aug 2023 15:12:23 -0500 Subject: [PATCH 02/13] system info entities updated --- .../adapters.systemInfo.controllers.web.ts | 8 ------ .../systemInfo/app.impl.systemInfo.ts | 9 ------ .../systemInfo/entities.systemInfo.ts | 28 ++++++++++++++++++- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/service/src/adapters/systemInfo/adapters.systemInfo.controllers.web.ts b/service/src/adapters/systemInfo/adapters.systemInfo.controllers.web.ts index 7b0cc995d..09440e4ec 100644 --- a/service/src/adapters/systemInfo/adapters.systemInfo.controllers.web.ts +++ b/service/src/adapters/systemInfo/adapters.systemInfo.controllers.web.ts @@ -16,14 +16,6 @@ export function SystemInfoRoutes(appLayer: SystemInfoAppLayer, createAppRequest: .get(async (req, res, next) => { const appReq = createAppRequest(req) - const permissionError = await appLayer.permissionsService.ensureReadSystemInfoPermission( - appReq.context - ); - - if (permissionError) { - return next(permissionError); - } - const appRes = await appLayer.readSystemInfo(appReq) if (appRes.success) { return res.json(appRes.success) diff --git a/service/src/app.impl/systemInfo/app.impl.systemInfo.ts b/service/src/app.impl/systemInfo/app.impl.systemInfo.ts index fae4a9120..df34cde0b 100644 --- a/service/src/app.impl/systemInfo/app.impl.systemInfo.ts +++ b/service/src/app.impl/systemInfo/app.impl.systemInfo.ts @@ -53,19 +53,10 @@ export function CreateReadSystemInfo( const permissionError = await permissions.ensureReadSystemInfoPermission( req.context ); - if (permissionError) { throw permissionError; } - const user = req.context.requestingPrincipal(); - if (user && user.username !== 'admin') { - throw permissionDenied( - SystemInfoPermission.READ_SYSTEM_INFO, - user.username, - 'SystemInfo' - ); - } // TODO: will need a permission check to determine what level of system // information the requesting principal is allowed to see diff --git a/service/src/entities/systemInfo/entities.systemInfo.ts b/service/src/entities/systemInfo/entities.systemInfo.ts index a75eecde5..b0833270c 100644 --- a/service/src/entities/systemInfo/entities.systemInfo.ts +++ b/service/src/entities/systemInfo/entities.systemInfo.ts @@ -25,4 +25,30 @@ export interface SystemInfo { environment: EnvironmentInfo disclaimer: any // mongoose Document type contactInfo: any -} \ No newline at end of file +} + +export enum SystemInfoRole { + ADMIN = 'ADMIN', + USER = 'USER', +} + +export enum SystemInfoAccessType { + Read = 'read', +} + +export type SystemInfoRolePermissions = { [role in SystemInfoRole]: SystemInfoAccessType[] } + +export const SystemInfoRolePermissions: SystemInfoRolePermissions = { + ADMIN: [ SystemInfoAccessType.Read ], + USER: [], +} + +export function rolesWithPermission(permission: SystemInfoAccessType): SystemInfoRole[] { + const roles: SystemInfoRole[] = []; + for (const key in SystemInfoRolePermissions) { + if (SystemInfoRolePermissions[key as SystemInfoRole].indexOf(permission) !== -1) { + roles.push(key as SystemInfoRole); + } + } + return roles; +} From f61b58996a9a113dcb941bf78dfaba9f9d8c8d0b Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Wed, 30 Aug 2023 17:43:47 -0500 Subject: [PATCH 03/13] removed redundant entities code --- .../systemInfo/entities.systemInfo.ts | 26 ---------------- .../src/permissions/permissions.systemInfo.ts | 31 +++++++++++++------ 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/service/src/entities/systemInfo/entities.systemInfo.ts b/service/src/entities/systemInfo/entities.systemInfo.ts index b0833270c..32e256887 100644 --- a/service/src/entities/systemInfo/entities.systemInfo.ts +++ b/service/src/entities/systemInfo/entities.systemInfo.ts @@ -26,29 +26,3 @@ export interface SystemInfo { disclaimer: any // mongoose Document type contactInfo: any } - -export enum SystemInfoRole { - ADMIN = 'ADMIN', - USER = 'USER', -} - -export enum SystemInfoAccessType { - Read = 'read', -} - -export type SystemInfoRolePermissions = { [role in SystemInfoRole]: SystemInfoAccessType[] } - -export const SystemInfoRolePermissions: SystemInfoRolePermissions = { - ADMIN: [ SystemInfoAccessType.Read ], - USER: [], -} - -export function rolesWithPermission(permission: SystemInfoAccessType): SystemInfoRole[] { - const roles: SystemInfoRole[] = []; - for (const key in SystemInfoRolePermissions) { - if (SystemInfoRolePermissions[key as SystemInfoRole].indexOf(permission) !== -1) { - roles.push(key as SystemInfoRole); - } - } - return roles; -} diff --git a/service/src/permissions/permissions.systemInfo.ts b/service/src/permissions/permissions.systemInfo.ts index f362cc8f0..a34177527 100644 --- a/service/src/permissions/permissions.systemInfo.ts +++ b/service/src/permissions/permissions.systemInfo.ts @@ -5,27 +5,40 @@ import { import { AppRequestContext } from '../app.api/app.api.global'; import { SystemInfoPermissionService } from '../app.api/systemInfo/app.api.systemInfo'; import { SystemInfoPermission } from '../entities/authorization/entities.permissions'; +import { rolesWithPermission, SystemInfoAccessType } from '../entities/systemInfo/entities.systemInfo'; import { UserWithRole, ensureContextUserHasPermission } from './permissions.role-based.base'; -export class RoleBasedSystemInfoPermissionService implements SystemInfoPermissionService { - async ensureReadSystemInfoPermission( ctx: AppRequestContext): Promise { +export class RoleBasedSystemInfoPermissionService + implements SystemInfoPermissionService { + async ensureReadSystemInfoPermission( + ctx: AppRequestContext + ): Promise { const user = ctx.requestingPrincipal(); - // Check if the user is the "admin" - if (user && user.username !== 'admin') { + // If user doesn't exist, deny permission. + if (!user) { return permissionDenied( SystemInfoPermission.READ_SYSTEM_INFO, - user.username, + 'Unknown User', 'SystemInfo' ); } - return ensureContextUserHasPermission( - ctx, - SystemInfoPermission.READ_SYSTEM_INFO - ); + // Check if the user's role has the required permission. + if ( + user.roleId.permissions.includes(SystemInfoPermission.READ_SYSTEM_INFO) + ) { + return null; // This means no error and the user has permission + } else { + return permissionDenied( + SystemInfoPermission.READ_SYSTEM_INFO, + user.username, + 'SystemInfo' + ); + } } } + From 422461ad45a6f73b93e1f0d82d53aab29feef35e Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Wed, 30 Aug 2023 17:44:57 -0500 Subject: [PATCH 04/13] remove unused imports --- service/src/permissions/permissions.systemInfo.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/service/src/permissions/permissions.systemInfo.ts b/service/src/permissions/permissions.systemInfo.ts index a34177527..c3f252ef4 100644 --- a/service/src/permissions/permissions.systemInfo.ts +++ b/service/src/permissions/permissions.systemInfo.ts @@ -5,14 +5,11 @@ import { import { AppRequestContext } from '../app.api/app.api.global'; import { SystemInfoPermissionService } from '../app.api/systemInfo/app.api.systemInfo'; import { SystemInfoPermission } from '../entities/authorization/entities.permissions'; -import { rolesWithPermission, SystemInfoAccessType } from '../entities/systemInfo/entities.systemInfo'; import { UserWithRole, - ensureContextUserHasPermission } from './permissions.role-based.base'; -export class RoleBasedSystemInfoPermissionService - implements SystemInfoPermissionService { +export class RoleBasedSystemInfoPermissionService implements SystemInfoPermissionService { async ensureReadSystemInfoPermission( ctx: AppRequestContext ): Promise { From 9c9ad1e7439047458cc9cace21216743129f1569 Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Wed, 30 Aug 2023 19:05:41 -0500 Subject: [PATCH 05/13] SystemInfoPermission included in allPermissions, formatting change --- .../app.api/systemInfo/app.api.systemInfo.ts | 4 +-- .../authorization/entities.permissions.ts | 25 ++++++++++--------- .../src/permissions/permissions.systemInfo.ts | 4 +-- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/service/src/app.api/systemInfo/app.api.systemInfo.ts b/service/src/app.api/systemInfo/app.api.systemInfo.ts index 016558df7..5c6e936fc 100644 --- a/service/src/app.api/systemInfo/app.api.systemInfo.ts +++ b/service/src/app.api/systemInfo/app.api.systemInfo.ts @@ -23,7 +23,5 @@ export interface SystemInfoAppLayer { } export interface SystemInfoPermissionService { - ensureReadSystemInfoPermission( - context: AppRequestContext - ): Promise; + ensureReadSystemInfoPermission(context: AppRequestContext): Promise; } \ No newline at end of file diff --git a/service/src/entities/authorization/entities.permissions.ts b/service/src/entities/authorization/entities.permissions.ts index d73302926..03cb97acd 100644 --- a/service/src/entities/authorization/entities.permissions.ts +++ b/service/src/entities/authorization/entities.permissions.ts @@ -95,18 +95,19 @@ export enum StaticIconPermission { } export const allPermissions = Object.freeze({ - ...DevicePermission, - ...UsersPermission, - ...RolePermission, - ...MageEventPermission, - ...LayerPermission, - ...ObservationPermission, - ...LocationPermission, - ...TeamPermission, - ...SettingPermission, - ...FeedsPermission, - ...StaticIconPermission -}) + ...DevicePermission, + ...UsersPermission, + ...RolePermission, + ...MageEventPermission, + ...LayerPermission, + ...ObservationPermission, + ...LocationPermission, + ...TeamPermission, + ...SettingPermission, + ...FeedsPermission, + ...StaticIconPermission, + ...SystemInfoPermission + }); export type AnyPermission = | DevicePermission diff --git a/service/src/permissions/permissions.systemInfo.ts b/service/src/permissions/permissions.systemInfo.ts index c3f252ef4..c4a31eac7 100644 --- a/service/src/permissions/permissions.systemInfo.ts +++ b/service/src/permissions/permissions.systemInfo.ts @@ -10,9 +10,7 @@ import { } from './permissions.role-based.base'; export class RoleBasedSystemInfoPermissionService implements SystemInfoPermissionService { - async ensureReadSystemInfoPermission( - ctx: AppRequestContext - ): Promise { + async ensureReadSystemInfoPermission(ctx: AppRequestContext): Promise { const user = ctx.requestingPrincipal(); // If user doesn't exist, deny permission. From 30a43472757998e27d11629f033656daf2d8d374 Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Thu, 31 Aug 2023 08:51:10 -0500 Subject: [PATCH 06/13] rolebased sysinfo now sysinfo permission service --- .../systemInfo/app.impl.systemInfo.ts | 3 +- .../src/permissions/permissions.systemInfo.ts | 29 ++------ .../permissions.systemInfo.test.ts | 67 +++++++++++++++++++ 3 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 service/test/permissions/permissions.systemInfo.test.ts diff --git a/service/src/app.impl/systemInfo/app.impl.systemInfo.ts b/service/src/app.impl/systemInfo/app.impl.systemInfo.ts index df34cde0b..4ad12840f 100644 --- a/service/src/app.impl/systemInfo/app.impl.systemInfo.ts +++ b/service/src/app.impl/systemInfo/app.impl.systemInfo.ts @@ -7,6 +7,7 @@ import AuthenticationConfigurationTransformer from '../../transformers/authentic import { RoleBasedSystemInfoPermissionService } from '../../permissions/permissions.systemInfo'; import { permissionDenied } from '../../app.api/app.api.errors'; import { SystemInfoPermission } from '../../entities/authorization/entities.permissions'; +import { SystemInfoPermissionService } from '../../app.api/systemInfo/app.api.systemInfo'; /** * This factory function creates the implementation of the {@link api.ReadSystemInfo} * application layer interface. @@ -17,7 +18,7 @@ export function CreateReadSystemInfo( settingsModule: typeof Settings = Settings, authConfigModule: typeof AuthenticationConfiguration = AuthenticationConfiguration, authConfigTransformerModule: typeof AuthenticationConfigurationTransformer = AuthenticationConfigurationTransformer, - permissions: RoleBasedSystemInfoPermissionService + permissions: SystemInfoPermissionService ): api.ReadSystemInfo { // appending the authentication strategies to the api diff --git a/service/src/permissions/permissions.systemInfo.ts b/service/src/permissions/permissions.systemInfo.ts index c4a31eac7..ac98ff502 100644 --- a/service/src/permissions/permissions.systemInfo.ts +++ b/service/src/permissions/permissions.systemInfo.ts @@ -7,33 +7,16 @@ import { SystemInfoPermissionService } from '../app.api/systemInfo/app.api.syste import { SystemInfoPermission } from '../entities/authorization/entities.permissions'; import { UserWithRole, + ensureContextUserHasPermission } from './permissions.role-based.base'; export class RoleBasedSystemInfoPermissionService implements SystemInfoPermissionService { async ensureReadSystemInfoPermission(ctx: AppRequestContext): Promise { - const user = ctx.requestingPrincipal(); - - // If user doesn't exist, deny permission. - if (!user) { - return permissionDenied( - SystemInfoPermission.READ_SYSTEM_INFO, - 'Unknown User', - 'SystemInfo' - ); - } - - // Check if the user's role has the required permission. - if ( - user.roleId.permissions.includes(SystemInfoPermission.READ_SYSTEM_INFO) - ) { - return null; // This means no error and the user has permission - } else { - return permissionDenied( - SystemInfoPermission.READ_SYSTEM_INFO, - user.username, - 'SystemInfo' - ); - } + return ensureContextUserHasPermission( + ctx, + SystemInfoPermission.READ_SYSTEM_INFO + ); } } + diff --git a/service/test/permissions/permissions.systemInfo.test.ts b/service/test/permissions/permissions.systemInfo.test.ts new file mode 100644 index 000000000..00b3bc438 --- /dev/null +++ b/service/test/permissions/permissions.systemInfo.test.ts @@ -0,0 +1,67 @@ +import { expect } from 'chai'; +import { RoleBasedSystemInfoPermissionService } from '../../lib/permissions/permissions.systemInfo'; +import { AppRequestContext } from '../../lib/app.api/app.api.global'; +import { + allPermissions, + SystemInfoPermission +} from '../../lib/entities/authorization/entities.permissions'; +import { ErrPermissionDenied } from '../../lib/app.api/app.api.errors'; +import { UserWithRole } from '../../lib/permissions/permissions.role-based.base'; + +describe('system info role-based permission service', function() { + let permissions: RoleBasedSystemInfoPermissionService; + + beforeEach(function() { + permissions = new RoleBasedSystemInfoPermissionService(); + }); + + it('denies read permission if user does not have read system info permission', async function() { + const ctx: AppRequestContext = { + requestToken: Symbol(), + requestingPrincipal() { + return ({ + username: 'neverever', + roleId: { + permissions: [allPermissions.READ_OBSERVATION_ALL] + } + } as unknown) as UserWithRole; + }, + locale() { + return null; + } + }; + + const denied = await permissions.ensureReadSystemInfoPermission(ctx); + + expect(denied?.code).to.equal(ErrPermissionDenied); + expect(denied?.data.subject).to.equal('neverever'); + expect(denied?.data.permission).to.equal( + SystemInfoPermission.READ_SYSTEM_INFO + ); + expect(denied?.data.object).to.equal('SystemInfo'); + }); + + it('allows read permission if user has read system info permission', async function() { + const ctx: AppRequestContext = { + requestToken: Symbol(), + requestingPrincipal() { + return ({ + username: 'haspermission', + roleId: { + permissions: [ + allPermissions.READ_SYSTEM_INFO, + SystemInfoPermission.READ_SYSTEM_INFO + ] + } + } as unknown) as UserWithRole; + }, + locale() { + return null; + } + }; + + const denied = await permissions.ensureReadSystemInfoPermission(ctx); + + expect(denied).to.be.null; + }); +}); From 8a8f7b55426ad64739b6b491cbaca24f544dc0e3 Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Thu, 31 Aug 2023 14:06:27 -0500 Subject: [PATCH 07/13] only exclude the environment info if no permission --- .../systemInfo/app.impl.systemInfo.ts | 28 +++++------ service/src/permissions/permissions.icons.ts | 2 +- .../app/systemInfo/app.systemInfo.test.ts | 47 +++++++++++++++---- 3 files changed, 51 insertions(+), 26 deletions(-) diff --git a/service/src/app.impl/systemInfo/app.impl.systemInfo.ts b/service/src/app.impl/systemInfo/app.impl.systemInfo.ts index 4ad12840f..a5b7138fa 100644 --- a/service/src/app.impl/systemInfo/app.impl.systemInfo.ts +++ b/service/src/app.impl/systemInfo/app.impl.systemInfo.ts @@ -4,9 +4,7 @@ import { EnvironmentService } from '../../entities/systemInfo/entities.systemInf import * as Settings from '../../models/setting'; import * as AuthenticationConfiguration from '../../models/authenticationconfiguration'; import AuthenticationConfigurationTransformer from '../../transformers/authenticationconfiguration'; -import { RoleBasedSystemInfoPermissionService } from '../../permissions/permissions.systemInfo'; -import { permissionDenied } from '../../app.api/app.api.errors'; -import { SystemInfoPermission } from '../../entities/authorization/entities.permissions'; + import { SystemInfoPermissionService } from '../../app.api/systemInfo/app.api.systemInfo'; /** * This factory function creates the implementation of the {@link api.ReadSystemInfo} @@ -20,7 +18,6 @@ export function CreateReadSystemInfo( authConfigTransformerModule: typeof AuthenticationConfigurationTransformer = AuthenticationConfigurationTransformer, permissions: SystemInfoPermissionService ): api.ReadSystemInfo { - // appending the authentication strategies to the api async function appendAuthenticationStrategies( api: any, @@ -50,18 +47,14 @@ export function CreateReadSystemInfo( return async function readSystemInfo( req: api.ReadSystemInfoRequest ): Promise { + const hasReadSystemInfoPermission = + (await permissions.ensureReadSystemInfoPermission(req.context)) === null; - const permissionError = await permissions.ensureReadSystemInfoPermission( - req.context - ); - if (permissionError) { - throw permissionError; - } + let environment; + if (hasReadSystemInfoPermission) { + environment = await environmentService.readEnvironmentInfo(); + } - - // TODO: will need a permission check to determine what level of system - // information the requesting principal is allowed to see - const environment = await environmentService.readEnvironmentInfo(); const disclaimer = (await settingsModule.getSetting('disclaimer')) || {}; const contactInfo = (await settingsModule.getSetting('contactInfo')) || {}; @@ -71,10 +64,15 @@ export function CreateReadSystemInfo( contactInfo: contactInfo }); + // Ensure the environment is removed if the user doesn't have permission + if (!hasReadSystemInfoPermission) { + delete apiConfig.environment; + } + const updatedApiConfig = await appendAuthenticationStrategies(apiConfig, { whitelist: true }); return AppResponse.success(updatedApiConfig as any); }; -} \ No newline at end of file +} diff --git a/service/src/permissions/permissions.icons.ts b/service/src/permissions/permissions.icons.ts index fce26cab9..9f24b65c1 100644 --- a/service/src/permissions/permissions.icons.ts +++ b/service/src/permissions/permissions.icons.ts @@ -1,4 +1,4 @@ -import { permissionDenied, PermissionDeniedError } from '../app.api/app.api.errors' +import { PermissionDeniedError } from '../app.api/app.api.errors' import { AppRequestContext } from '../app.api/app.api.global' import { StaticIconPermissionService } from '../app.api/icons/app.api.icons' import { StaticIconPermission } from '../entities/authorization/entities.permissions' diff --git a/service/test/app/systemInfo/app.systemInfo.test.ts b/service/test/app/systemInfo/app.systemInfo.test.ts index 49d344192..99ade2b79 100644 --- a/service/test/app/systemInfo/app.systemInfo.test.ts +++ b/service/test/app/systemInfo/app.systemInfo.test.ts @@ -5,16 +5,13 @@ import { } from '../../../lib/entities/systemInfo/entities.systemInfo'; import { Substitute, Arg } from '@fluffy-spoon/substitute'; import { CreateReadSystemInfo } from '../../../lib/app.impl/systemInfo/app.impl.systemInfo'; -import { - AppRequest, - AppRequestContext -} from '../../../lib/app.api/app.api.global'; import * as Settings from '../../../lib/models/setting'; import * as AuthenticationConfiguration from '../../../lib/models/authenticationconfiguration'; import * as AuthenticationConfigurationTransformer from '../../../lib/transformers/authenticationconfiguration'; import { UserWithRole } from '../../../lib/permissions/permissions.role-based.base'; import { ReadSystemInfo, ReadSystemInfoRequest } from '../../../lib/app.api/systemInfo/app.api.systemInfo'; import { RoleBasedSystemInfoPermissionService } from '../../../lib/permissions/permissions.systemInfo'; +import { SystemInfoPermission } from '../../../lib/entities/authorization/entities.permissions'; const mockNodeVersion = '14.16.1'; const mockMongoDBVersion = '4.2.0'; @@ -34,16 +31,37 @@ const mockUserWithRole = ({ phones: [], active: true, enabled: true, - roleId: 'mockRoleId', + roleId: { + id: 'mockRoleId', + permissions: [SystemInfoPermission.READ_SYSTEM_INFO] // Given the role the necessary permission. + }, authenticationId: 'mockAuthId', recentEventIds: [], createdAt: new Date(), lastUpdated: new Date() } as unknown) as UserWithRole; +const mockUserWithoutPermission = ({ + _id: 'mockObjectId2', + id: 'testUserId2', + username: 'testUser2', + displayName: 'Test User 2', + phones: [], + active: true, + enabled: true, + roleId: { + id: 'mockNoPermissionRoleId', + permissions: [] // No permissions. + }, + authenticationId: 'mockAuthId2', + recentEventIds: [], + createdAt: new Date(), + lastUpdated: new Date() +} as unknown) as UserWithRole; + function requestBy( - principal: UserWithRole, // assuming you have some way to produce or mock a UserWithRole + principal: UserWithRole, params?: T ): ReadSystemInfoRequest & T { if (!params) { @@ -84,10 +102,6 @@ describe('CreateReadSystemInfo', () => { }; const mockedPermissionsModule = new RoleBasedSystemInfoPermissionService(); - // mockedPermissionsModule - // .checkPermission(Arg.any()) - // .returns(Promise.resolve(true)); - beforeEach(() => { mockedSettingsModule = Substitute.for(); @@ -148,4 +162,17 @@ describe('CreateReadSystemInfo', () => { const versionFormat = /^\d+\.\d+\.\d+$/; expect(nodeVersion).to.match(versionFormat); }); + + it('should return a function that produces a ReadSystemInfoResponse without environment info for users without permission', async () => { + const request = requestBy(mockUserWithoutPermission); + const response = await readSystemInfo(request); + + expect(response).to.exist; + expect(response.success).to.exist; + + const systemInfo: SystemInfo = response.success as SystemInfo; + + expect(systemInfo.environment).to.be.undefined; // Asserts that environment info is not present + }); + }); From 44f80a8a5d3ce49193126c02f5a52b33327d07b5 Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Thu, 31 Aug 2023 15:33:55 -0500 Subject: [PATCH 08/13] read system info in create-admin-role migration --- service/src/migrations/003-create-admin-role.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/src/migrations/003-create-admin-role.js b/service/src/migrations/003-create-admin-role.js index ae4490b68..19638b080 100644 --- a/service/src/migrations/003-create-admin-role.js +++ b/service/src/migrations/003-create-admin-role.js @@ -11,7 +11,7 @@ exports.up = function(done) { 'CREATE_USER', 'READ_USER', 'UPDATE_USER', 'DELETE_USER', 'CREATE_ROLE', 'READ_ROLE', 'UPDATE_ROLE', 'DELETE_ROLE', 'CREATE_EVENT', 'READ_EVENT_ALL', 'UPDATE_EVENT', 'DELETE_EVENT', - 'CREATE_LAYER', 'READ_LAYER_ALL', 'UPDATE_LAYER', 'DELETE_LAYER', + 'CREATE_LAYER', 'READ_LAYER_ALL', 'READ_SYSTEM_INFO','UPDATE_LAYER', 'DELETE_LAYER', 'CREATE_OBSERVATION', 'READ_OBSERVATION_ALL', 'UPDATE_OBSERVATION_ALL', 'DELETE_OBSERVATION', 'CREATE_LOCATION', 'READ_LOCATION_ALL', 'UPDATE_LOCATION_ALL', 'DELETE_LOCATION', 'CREATE_TEAM', 'READ_TEAM', 'UPDATE_TEAM', 'DELETE_TEAM']; From aa6a4c2389d63aaa5eb19c72e96d6769de8d3ea6 Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Thu, 31 Aug 2023 15:42:44 -0500 Subject: [PATCH 09/13] update Pending on Develop in changlog --- CHANGELOG.md | 702 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 412 insertions(+), 290 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e318871b0..ebf3f8bba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,113 +1,154 @@ # Change Log + All notable changes to this project will be documented in this file. MAGE adheres to [Semantic Versioning](http://semver.org/). --- + ## Pending on [`develop`](https://github.com/ngageoint/mage-server/tree/develop) ##### Features -* Support for Mongoose 6.x. -* [mongodb-migrations](https://github.com/gillandk/mongodb-migrations) support for Mongo 4.x. -* Support for Webpack 5.x. -* Support for Angular 14.x. -* MAGE_MONGO_TLS_INSECURE added as env var to help with possible issues with [self-signed certs](https://github.com/Automattic/mongoose/issues/9147). -* [GARS](https://github.com/ngageoint/gars-js) grid overlay. -* [MGRS](https://github.com/ngageoint/mgrs-js) grid overlay. + +- **System Info**: + - Introduced a new section to display system information on the about page. Provides users with insights into system-related details. +- **Permissions**: + - Enhanced the role-based access control by introducing new permissions related to system info: + - `READ_SYSTEM_INFO`: Allows users with the right roles to view system information. +- Support for Mongoose 6.x. +- [mongodb-migrations](https://github.com/gillandk/mongodb-migrations) support for Mongo 4.x. +- Support for Webpack 5.x. +- Support for Angular 14.x. +- MAGE_MONGO_TLS_INSECURE added as env var to help with possible issues with [self-signed certs](https://github.com/Automattic/mongoose/issues/9147). +- [GARS](https://github.com/ngageoint/gars-js) grid overlay. +- [MGRS](https://github.com/ngageoint/mgrs-js) grid overlay. ##### Bug fixes -* Fix single observation download. -* Protect against disabling all authentications. -* Fixing problem with oauth web login. + +- Fix single observation download. +- Protect against disabling all authentications. +- Fixing problem with oauth web login. ## [6.2.9](https://github.com/ngageoint/mage-server/releases/tag/6.2.9) + #### Features -* Docker image now installs MAGE packages from NPM registry instead of local tarballs. -* Use a [shrinkwrap](https://docs.npmjs.com/cli/v9/commands/npm-shrinkwrap) file for consistent `@ngageoint/mage.service` installs. + +- Docker image now installs MAGE packages from NPM registry instead of local tarballs. +- Use a [shrinkwrap](https://docs.npmjs.com/cli/v9/commands/npm-shrinkwrap) file for consistent `@ngageoint/mage.service` installs. #### Bug Fixes -* The server was not saving location provider information, such as accuracy and location source, e.g., `gps` or `manual`. -* Previous 6.2.x releases were missing bug fixes from [6.1.3](#6.1.3) and [6.1.2](#6.1.2). -* The service package did not always install [better-sqlite3](https://www.npmjs.com/package/better-sqlite3), which [GeoPackage](https://www.npmjs.com/package/@ngageoint/geopackage) requires to perform operations in the Node.js runtime. + +- The server was not saving location provider information, such as accuracy and location source, e.g., `gps` or `manual`. +- Previous 6.2.x releases were missing bug fixes from [6.1.3](#6.1.3) and [6.1.2](#6.1.2). +- The service package did not always install [better-sqlite3](https://www.npmjs.com/package/better-sqlite3), which [GeoPackage](https://www.npmjs.com/package/@ngageoint/geopackage) requires to perform operations in the Node.js runtime. ## [6.2.8](https://github.com/ngageoint/mage-server/releases/tag/6.2.8) + #### Features + #### Bug Fixes -* Numeric form field validation failed when the field was required and the + +- Numeric form field validation failed when the field was required and the field value was zero. ## [6.2.7](https://github.com/ngageoint/mage-server/releases/tag/6.2.7) + #### Features + #### Bug Fixes -* In Node 18, GeoPackage operations threw an error, `Error: Unable to initialize canvas`. + +- In Node 18, GeoPackage operations threw an error, `Error: Unable to initialize canvas`. The library was updated to version 4.2.4 to resolve the error. ## [6.2.6](https://github.com/ngageoint/mage-server/releases/tag/6.2.6) + #### Features + #### Bug Fixes -* Return 404 status reponse for empty geopackage tile generation. -* Return feature and tile tables from geopackage utility getTables method. + +- Return 404 status reponse for empty geopackage tile generation. +- Return feature and tile tables from geopackage utility getTables method. ## [6.2.5](https://github.com/ngageoint/mage-server/releases/tag/6.2.5) + #### Features + #### Bug Fixes -* The observation download API incorrectly imported a library function causing a bad reference. + +- The observation download API incorrectly imported a library function causing a bad reference. ## [6.2.4](https://github.com/ngageoint/mage-server/releases/tag/6.2.4) + #### Features -* The Degrees-Minutes-Seconds (DMS) location form fields on the Observation Edit view now use an input mask for more natural manual entry. + +- The Degrees-Minutes-Seconds (DMS) location form fields on the Observation Edit view now use an input mask for more natural manual entry. + #### Bug Fixes -* The DMS location form allows typing leading zeros in all the coordinate parts. -* The DMS form parses both latitude and longitude from pasted text if available. -* Eliminate inconsistencies between DMS parsing and validation. + +- The DMS location form allows typing leading zeros in all the coordinate parts. +- The DMS form parses both latitude and longitude from pasted text if available. +- Eliminate inconsistencies between DMS parsing and validation. ## [6.2.3](https://github.com/ngageoint/mage-server/releases/tag/6.2.3) + ##### Features + ##### Bug Fixes -* Fix user role translation + +- Fix user role translation ## [6.2.2](https://github.com/ngageoint/mage-server/releases/tag/6.2.2) + ##### Features -* Allow WebP and GIF image attachments on observations. + +- Allow WebP and GIF image attachments on observations. ##### Bug Fixes -* Redact passwords from user JSON documents with local authentication configured. -* A corrupted data scenario when a user's related authentication document was missing crashed the server. + +- Redact passwords from user JSON documents with local authentication configured. +- A corrupted data scenario when a user's related authentication document was missing crashed the server. ## [6.2.1](https://github.com/ngageoint/mage-server/releases/tag/6.2.1) + ##### Features + ##### Bug Fixes -* Image Plugin - mark attachments oriented on failures to avoid unlimited retries. -* A query parameter in the new user search was wrong. -* Remove all dots from web plugin IDs so UI Router does not interpret them as route hierarchy. -* Do not enforce form min/max validation constraints for archived forms. -* Restore missing view templates for auth and observation pages. + +- Image Plugin - mark attachments oriented on failures to avoid unlimited retries. +- A query parameter in the new user search was wrong. +- Remove all dots from web plugin IDs so UI Router does not interpret them as route hierarchy. +- Do not enforce form min/max validation constraints for archived forms. +- Restore missing view templates for auth and observation pages. ## [6.2.0](https://github.com/ngageoint/mage-server/releases/tag/6.2.0) + ##### Breaking Changes -* MAGE now requires Node.js >= 14. Node <= 12 is end-of-life. Node >= 16 is - recommended. See https://nodejs.org/en/about/releases/. + +- MAGE now requires Node.js >= 14. Node <= 12 is end-of-life. Node >= 16 is + recommended. See https://nodejs.org/en/about/releases/. ##### Features -* External data feeds - Integrate relevant data from external sources on your + +- External data feeds - Integrate relevant data from external sources on your MAGE map and list views. -* Configurable admin contact - Admins can now configure the admin contact +- Configurable admin contact - Admins can now configure the admin contact information the MAGE apps present to users when an error occurs. -* Hooks for localization support based on `accept-language` HTTP header -* New architecture with dependency injection and plugin APIs -* TypeScript +- Hooks for localization support based on `accept-language` HTTP header +- New architecture with dependency injection and plugin APIs +- TypeScript ##### Bug Fixes -* Minor UI fixes for the export dialog -* GeoPackage exports now set contents bounds correctly; previously the + +- Minor UI fixes for the export dialog +- GeoPackage exports now set contents bounds correctly; previously the export set the bounds to the entire world. -* Improved memory consumption for all export types to avoid out-of-memory errors. -* Removed reference to deprecated `request` package and corresponding missing +- Improved memory consumption for all export types to avoid out-of-memory errors. +- Removed reference to deprecated `request` package and corresponding missing reference bug in production build. -* Removing attachments from observations by individual field or form entry did +- Removing attachments from observations by individual field or form entry did not remove attachment meta-data from the observation attachments list. -* Removing attachments from observations by individual field or form entry did +- Removing attachments from observations by individual field or form entry did not remove the associated attachment files from the file system. -* Layer permissions bug prevented non-admin event participants from accessing +- Layer permissions bug prevented non-admin event participants from accessing layers. ## [6.1.3](https://github.com/ngageoint/mage-server/releases/tag/6.1.3) @@ -115,68 +156,77 @@ MAGE adheres to [Semantic Versioning](http://semver.org/). ##### Features ##### Bug Fixes -* New user sign-ups failed when the admin approval setting was disabled and event auto-assignment was configured for new users. -* Web app incorrectly indicated new accounts required admin approval after sign-up even when the admin approval setting was disabled. + +- New user sign-ups failed when the admin approval setting was disabled and event auto-assignment was configured for new users. +- Web app incorrectly indicated new accounts required admin approval after sign-up even when the admin approval setting was disabled. ## [6.1.2](https://github.com/ngageoint/mage-server/releases/tag/6.1.2) ##### Features ##### Bug Fixes -* Geopackage export will skip adding userId or deviceId if missing from observation. -* All export types will skip export of attachments missing relativePath property. + +- Geopackage export will skip adding userId or deviceId if missing from observation. +- All export types will skip export of attachments missing relativePath property. ## [6.1.1](https://github.com/ngageoint/mage-server/releases/tag/6.1.1) ##### Features ##### Bug Fixes -* GeoPackage export will skip adding user ID or device ID if missing from observation. -* KML will skip export of attachments missing relativePath property. + +- GeoPackage export will skip adding user ID or device ID if missing from observation. +- KML will skip export of attachments missing relativePath property. ## [6.1.0](https://github.com/ngageoint/mage-server/releases/tag/6.1.0) ##### Features -* You can now enter and display all coordinates in the app in Degree Minute Second format. -* Administrators can now allow for any attachment types, or restrict to image, video or audio. + +- You can now enter and display all coordinates in the app in Degree Minute Second format. +- Administrators can now allow for any attachment types, or restrict to image, video or audio. ##### Bug Fixes -* Text area form fields in observation forms are resizable. -* User and team paging API update to fix team and event size limitation. + +- Text area form fields in observation forms are resizable. +- User and team paging API update to fix team and event size limitation. ## [6.0.2](https://github.com/ngageoint/mage-server/releases/tag/6.0.2) ##### Features ##### Bug Fixes -* Fix observation view error on invalid primary/secondary feed field. -* Preserve primary and secondary feed fields on form import. -* Fix feed item preview, account for feed item without attachments. -* Fix form preview on event admin page. + +- Fix observation view error on invalid primary/secondary feed field. +- Preserve primary and secondary feed fields on form import. +- Fix feed item preview, account for feed item without attachments. +- Fix form preview on event admin page. ## [6.0.1](https://github.com/ngageoint/mage-server/releases/tag/6.0.1) ##### Features -* Shapefile export has been removed, please use GeoPackage export. -* Updated all export formats to account for multiple observation forms. + +- Shapefile export has been removed, please use GeoPackage export. +- Updated all export formats to account for multiple observation forms. ##### Bug Fixes ## [6.0.0](https://github.com/ngageoint/mage-server/releases/tag/6.0.0) ### Release Notes + **This release includes database migrations, please remember to backup your database before upgrading.** **If you are using geoaxis or google auth, you will need to login with a mage username/password account and follow the new setup in Adminstration options -> settings -> authentication.** ##### Features -* Multi form support. Users will be able to add multiple forms to an observation when the server configuration allows. Administrators can restrict total amount of forms, as well as min/max for individual forms. -* Attachments are now form fields. All existing forms will be migrated to include and "Attachments" form field as the first first field in each form. Administrators can edit forms to include any number of attachments fields. In addition administrators can restrict the number of attachments allowed in each field as well as the types of attachments. -* Local user signup captcha. All new local users will need to enter a captcha to create a local MAGE account. -* New authentication functionality ability under admin->settings (e.g. create, edit, etc.). -* Moving security settings to more secure location. -* Adding support for connecting to a generic OAuth server. -* Adding support for connecting to an OpenID Connect server. + +- Multi form support. Users will be able to add multiple forms to an observation when the server configuration allows. Administrators can restrict total amount of forms, as well as min/max for individual forms. +- Attachments are now form fields. All existing forms will be migrated to include and "Attachments" form field as the first first field in each form. Administrators can edit forms to include any number of attachments fields. In addition administrators can restrict the number of attachments allowed in each field as well as the types of attachments. +- Local user signup captcha. All new local users will need to enter a captcha to create a local MAGE account. +- New authentication functionality ability under admin->settings (e.g. create, edit, etc.). +- Moving security settings to more secure location. +- Adding support for connecting to a generic OAuth server. +- Adding support for connecting to an OpenID Connect server. ##### Bug Fixes @@ -185,379 +235,433 @@ MAGE adheres to [Semantic Versioning](http://semver.org/). ##### Features ##### Bug Fixes -* GeoPackage export properly formats observation form data to allowed geopackage types. -* Fix invalid reference in export startup service. -* KML user location export properly groups user locations. + +- GeoPackage export properly formats observation form data to allowed geopackage types. +- Fix invalid reference in export startup service. +- KML user location export properly groups user locations. ## [5.5.1](https://github.com/ngageoint/mage-server/releases/tag/5.5.1) ##### Features ##### Bug Fixes -* Fixed attachment upload regression when creating a new observation. -* Fixed user role not seeing observation details -* Fixed admin observation delete button missing + +- Fixed attachment upload regression when creating a new observation. +- Fixed user role not seeing observation details +- Fixed admin observation delete button missing ## [5.5.0](https://github.com/ngageoint/mage-server/releases/tag/5.5.0) ##### Features -* Export as GeoPackage. -* New export UI, allowing users to view previous exports. -* Exports are now done in the background, this will eliminate client timeouts for larger exports. -* Minor performance enhancements to existing export types. -* Adding icons to search results on admin pages. + +- Export as GeoPackage. +- New export UI, allowing users to view previous exports. +- Exports are now done in the background, this will eliminate client timeouts for larger exports. +- Minor performance enhancements to existing export types. +- Adding icons to search results on admin pages. ##### Bug Fixes -* Fix bug detecting invalid KML files on upload in some web clients. -* Login search on device page correctly filters on device. -* Display device uid, not user-agent, when filtering on devices from admin dashboard. -* Default admin approval as enabled for new user accounts. This was causing new user account creation to fail. -* User map icon now appears on the map. Default icon is also displayed instead of a missing icon image. -* Fixing grammar and misspellings on various admin pages. + +- Fix bug detecting invalid KML files on upload in some web clients. +- Login search on device page correctly filters on device. +- Display device uid, not user-agent, when filtering on devices from admin dashboard. +- Default admin approval as enabled for new user accounts. This was causing new user account creation to fail. +- User map icon now appears on the map. Default icon is also displayed instead of a missing icon image. +- Fixing grammar and misspellings on various admin pages. ## [5.4.4](https://github.com/ngageoint/mage-server/releases/tag/5.4.4) ##### Features -* Support for "Feature Style" extension, upgrade to newest version of GeoPackage JS. + +- Support for "Feature Style" extension, upgrade to newest version of GeoPackage JS. ##### Bug Fixes -* Change Graphics Magick call to orient image attachments such that exif metadata is not lost. -* Web should not prompt for device uid, if device admin approval is not enabled. -* Fix bug in 3rd party authentication which was not properly adding authenticated users. + +- Change Graphics Magick call to orient image attachments such that exif metadata is not lost. +- Web should not prompt for device uid, if device admin approval is not enabled. +- Fix bug in 3rd party authentication which was not properly adding authenticated users. ## [5.4.3](https://github.com/ngageoint/mage-server/releases/tag/5.4.3) #### Release Notes -* This release includes database migrations, please remember to backup your database before upgrading. + +- This release includes database migrations, please remember to backup your database before upgrading. ##### Features -* Optimize observation and user location responses. Created new APIs to populate observation and location user information and removed individual calls to get users. -* Admins can now setup a more robust password policy, see Admin -> Settings -> Local Authentication. -* First 10 results are shown for controls using typeahead feature (e.g. logins, adding users to teams, etc.). + +- Optimize observation and user location responses. Created new APIs to populate observation and location user information and removed individual calls to get users. +- Admins can now setup a more robust password policy, see Admin -> Settings -> Local Authentication. +- First 10 results are shown for controls using typeahead feature (e.g. logins, adding users to teams, etc.). ##### Bug Fixes -* Multiple users can be added to a team and/or event without refreshing. -* Users and Teams can be removed from events. -* Removing a user from a team will no longer take you to the user page. -* Display names are shown when users are added to the ACL. -* Fix swagger authentication token injection. -* Observation export will no longer fail if attachment file is missing from file system. + +- Multiple users can be added to a team and/or event without refreshing. +- Users and Teams can be removed from events. +- Removing a user from a team will no longer take you to the user page. +- Display names are shown when users are added to the ACL. +- Fix swagger authentication token injection. +- Observation export will no longer fail if attachment file is missing from file system. ## [5.4.2](https://github.com/ngageoint/mage-server/releases/tag/5.4.2) ##### Features ##### Bug Fixes -* Fix login after initial account setup. -* Fix export important, favorite and attachment filters. + +- Fix login after initial account setup. +- Fix export important, favorite and attachment filters. ## [5.4.1](https://github.com/ngageoint/mage-server/releases/tag/5.4.1) ##### Features -* Added observation location provider and location accuracy exports. + +- Added observation location provider and location accuracy exports. ##### Bug Fixes -* Fixed bug causing observation and location exports to fail. -* Fixed incorrect timestamp when using local timezone for observation and location exports. -* Fixed bug causing incorrect locations to be returned when using time filter and exporting both observations and locations. + +- Fixed bug causing observation and location exports to fail. +- Fixed incorrect timestamp when using local timezone for observation and location exports. +- Fixed bug causing incorrect locations to be returned when using time filter and exporting both observations and locations. ## [5.4.0](https://github.com/ngageoint/mage-server/releases/tag/5.4.0) ##### Features -* Added pagination for users, teams, events, devices and layers. This will greatly decrease load times for admin pages on servers with more users. -* Added observation location accuracy. -* Added support in configuration/environment for MongoDB replica sets. + +- Added pagination for users, teams, events, devices and layers. This will greatly decrease load times for admin pages on servers with more users. +- Added observation location accuracy. +- Added support in configuration/environment for MongoDB replica sets. ##### Bug Fixes -* Layer panel now properly removes layers when switching events. -* Hide admin icon in navbar for non admin users. -* New users created by admin should default to 'active'. -* Docker build now works with Angular CLI -* Fixed swagger page. -* Date/Time fields honor required attribute on client. + +- Layer panel now properly removes layers when switching events. +- Hide admin icon in navbar for non admin users. +- New users created by admin should default to 'active'. +- Docker build now works with Angular CLI +- Fixed swagger page. +- Date/Time fields honor required attribute on client. ## [5.3.5](https://github.com/ngageoint/mage-server/releases/tag/5.3.5) ##### Features -* Added administrative settings to enable automatic approval of new user accounts and devices. If you are using a third party authentication strategy + +- Added administrative settings to enable automatic approval of new user accounts and devices. If you are using a third party authentication strategy where user accounts have already been vetted, you can reduce the barrier to entry into MAGE by using this setting to automatically approve user accounts. - In addition you can reduce the device admin approval barrier to entry by automatically approving all new devices. Administrators can still + In addition you can reduce the device admin approval barrier to entry by automatically approving all new devices. Administrators can still disable devices for any reason, therby removing access to MAGE for that device. -* Improved map layers panel. Drag and drop layers to change map z-index, change layer opacity, zoom to layer bounds, and style feature layers. -* GeoPackage upgrade and optimizations. Feature tiles are now created server side, reducing load on browser. -* Observation view/edit header is sticky and will not scroll with content. -* New Material Design Leaflet map buttons. -* LDAP authentication support. -* SAML authentication support. - -##### Bug Fixes -* Add filter support to edit observation select and multiselect fields. -* Fix mobile web export. -* Fix observation download bug. -* Fix bug where unregistered devices were not shown on admin dashboard. -* Fix WMS layer getcapabilites fetch request when creating new WMS layer. -* Form create modal would sometimes generate an invalid random color. -* Fix bug that could cause iOS GeoPackage downloads to hang. -* Preserve line breaks and whitespace in textarea fields. +- Improved map layers panel. Drag and drop layers to change map z-index, change layer opacity, zoom to layer bounds, and style feature layers. +- GeoPackage upgrade and optimizations. Feature tiles are now created server side, reducing load on browser. +- Observation view/edit header is sticky and will not scroll with content. +- New Material Design Leaflet map buttons. +- LDAP authentication support. +- SAML authentication support. + +##### Bug Fixes + +- Add filter support to edit observation select and multiselect fields. +- Fix mobile web export. +- Fix observation download bug. +- Fix bug where unregistered devices were not shown on admin dashboard. +- Fix WMS layer getcapabilites fetch request when creating new WMS layer. +- Form create modal would sometimes generate an invalid random color. +- Fix bug that could cause iOS GeoPackage downloads to hang. +- Preserve line breaks and whitespace in textarea fields. ## [5.3.4](https://github.com/ngageoint/mage-server/releases/tag/5.3.4) ##### Features ##### Bug Fixes -* Fixed #70 + +- Fixed #70 ## [5.3.3](https://github.com/ngageoint/mage-server/releases/tag/5.3.3) ##### Features -* Geometry edit moved to main map to provide more space to edit complex geometries while retaining the context of other user data on the map. -* Added export option to exclude observation attachments. + +- Geometry edit moved to main map to provide more space to edit complex geometries while retaining the context of other user data on the map. +- Added export option to exclude observation attachments. ##### Bug Fixes -* Fixed bug parsing KML polygon and polyline styles. -* Improve error checking for invalid event form upload archives. -* Fix lag on observation delete. + +- Fixed bug parsing KML polygon and polyline styles. +- Improve error checking for invalid event form upload archives. +- Fix lag on observation delete. ## [5.3.2](https://github.com/ngageoint/mage-server/releases/tag/5.3.2) ##### Bug Fixes -* Bundle and host the Material Design CSS and fonts instead of pulling them -from the Google CDN so the MAGE webapp does not need an Internet connection. + +- Bundle and host the Material Design CSS and fonts instead of pulling them + from the Google CDN so the MAGE webapp does not need an Internet connection. ## [5.3.1](https://github.com/ngageoint/mage-server/releases/tag/5.3.1) ##### Features -* Bulk user import UI rework. + +- Bulk user import UI rework. ##### Bug Fixes -* KML import file browser fixed. -* Update express default template renderer directory and remove pug specific rendering. -* Uploaded observation attachments preserve filename property after multer upgrade. + +- KML import file browser fixed. +- Update express default template renderer directory and remove pug specific rendering. +- Uploaded observation attachments preserve filename property after multer upgrade. ## [5.3.0](https://github.com/ngageoint/mage-server/releases/tag/5.3.0) ##### Features -* GeoServer plugin which creates OGC WMS/WFS endpoints from MAGE. -* Sort observation fields in CSV export in form order. -* Upgrade multer, file uploads are now configured per route. -* Display for the feed can now be configured per form -* Updated to use material design. -* Observation new and edit views moved outside of observation feed. -* Added user information view. -* New login and signup page flow! + +- GeoServer plugin which creates OGC WMS/WFS endpoints from MAGE. +- Sort observation fields in CSV export in form order. +- Upgrade multer, file uploads are now configured per route. +- Display for the feed can now be configured per form +- Updated to use material design. +- Observation new and edit views moved outside of observation feed. +- Added user information view. +- New login and signup page flow! ##### Bug Fixes -* Fix KML to GeoJSON icon style. -* Export observations based on timestamp, not last updated. -* CSV export properly handles commas in values. -* Fix permission error when event user posts recent event. -* Fix bug preventing single observation download from web. -* Fix issue with form fields save marking form as dirty after save if it contains a user select field. + +- Fix KML to GeoJSON icon style. +- Export observations based on timestamp, not last updated. +- CSV export properly handles commas in values. +- Fix permission error when event user posts recent event. +- Fix bug preventing single observation download from web. +- Fix issue with form fields save marking form as dirty after save if it contains a user select field. ## [5.2.6](https://github.com/ngageoint/mage-server/releases/tag/5.2.6) ##### Features -* Increase JSON upload limit. -* Form import now supports historical forms. + +- Increase JSON upload limit. +- Form import now supports historical forms. ##### Bug Fixes -* Select field option delete now removes correct option. -* Select field reorder now correctly highlights reordered field. -* Don't hide required asterisk on form field title if field has a value. -* Remove user token when user is disabled. -* Separated geoaxis authentication url from api url + +- Select field option delete now removes correct option. +- Select field reorder now correctly highlights reordered field. +- Don't hide required asterisk on form field title if field has a value. +- Remove user token when user is disabled. +- Separated geoaxis authentication url from api url ## [5.2.5](https://github.com/ngageoint/mage-server/releases/tag/5.2.5) ##### Features -* Added Ubuntu upstart scripts + +- Added Ubuntu upstart scripts ##### Bug Fixes -* Fix form preview in admin event page. -* Sort observation form fields in KML export. -* Catch 'disconnect' event on mage-image child process and shutdown. This should prevent the mage-image process from being orphaned. + +- Fix form preview in admin event page. +- Sort observation form fields in KML export. +- Catch 'disconnect' event on mage-image child process and shutdown. This should prevent the mage-image process from being orphaned. ## [5.2.4](https://github.com/ngageoint/mage-server/releases/tag/5.2.4) ##### Features -* Added user create permission to EVENT_ADMIN_ROLE. + +- Added user create permission to EVENT_ADMIN_ROLE. ##### Bug Fixes -* Fix geometry view/edit field. -* Update file size metadata for uploaded GeoPackage after it is indexed. -* Add baseUrl to avatar and icon for team route when populating users. -* Upgrade GeoPackageJS libs to fix a problem creating vector tiles. + +- Fix geometry view/edit field. +- Update file size metadata for uploaded GeoPackage after it is indexed. +- Add baseUrl to avatar and icon for team route when populating users. +- Upgrade GeoPackageJS libs to fix a problem creating vector tiles. ## [5.2.3](https://github.com/ngageoint/mage-server/releases/tag/5.2.3) ##### Features -* Added new environment variable and configuration to support secure login session cookie. NOTE: - the MAGE login session is a very short lived session that exists between valid authentication and - device id authorization. + +- Added new environment variable and configuration to support secure login session cookie. NOTE: + the MAGE login session is a very short lived session that exists between valid authentication and + device id authorization. ##### Bug Fixes -* Work around for leaflet GridLayer space between tiles. -* Fixed bug preventing navbar options from displaying all elements on mobile. + +- Work around for leaflet GridLayer space between tiles. +- Fixed bug preventing navbar options from displaying all elements on mobile. ## [5.2.2](https://github.com/ngageoint/mage-server/releases/tag/5.2.2) ##### Features ##### Bug Fixes -* Fixed bug with date/time fields not saving. + +- Fixed bug with date/time fields not saving. ## [5.2.1](https://github.com/ngageoint/mage-server/releases/tag/5.2.1) ##### Features -* New more featureful geocoder control + +- New more featureful geocoder control ##### Bug Fixes -* Fix KML export for events with no forms. -* Fix bug in MGRS display for lines and polygons. -* Fix username/password error message on invalid login when account lock is disabled. -* Fix bug in observation edit where initial timezone format was not being picked up. -* Fix bug in shapefile column name that esri could not read. + +- Fix KML export for events with no forms. +- Fix bug in MGRS display for lines and polygons. +- Fix username/password error message on invalid login when account lock is disabled. +- Fix bug in observation edit where initial timezone format was not being picked up. +- Fix bug in shapefile column name that esri could not read. ## [5.2.0](https://github.com/ngageoint/mage-server/releases/tag/5.2.0) ##### Features -* Users can view or edit coordinates as MGRS. -* Users can edit observation time in GMT or Local time. -* Users can view observation and location time as relative or absolute local or GMT. -* Separate authentication from authorization. Users will now be prompted to enter a device after after successful authentication. + +- Users can view or edit coordinates as MGRS. +- Users can edit observation time in GMT or Local time. +- Users can view observation and location time as relative or absolute local or GMT. +- Separate authentication from authorization. Users will now be prompted to enter a device after after successful authentication. ##### Bug Fixes ## [5.1.4](https://github.com/ngageoint/mage-server/releases/tag/5.1.4) ##### Features -* Added GeoPackage layer support. Added server side XYZ urls to retrieve imagery from GeoPackages. The web client will use these URLs to display imagery tiles from a GeoPackage. Added server side url to retrieve vector tiles from feature GeoPackages. The web client will use these to display vector tiles (with the aid of a leaflet plugin). -* User account lock settings. Admins can now configure account lock/disable settings for local accounts. -* Replace local [environment](environment) NPM packages with a single Node module -** No more manually deleting the local module from the `node_modules` directory for script changes -* Load values from [environment variables](README.md#mage-environment-settings) instead of only from the script -** No more copying the modified environment script when [upgrading](README.md#upgrading-mage-server) the server -* Continuously attempt to connect to MongoDB when the server app starts, exiting after a [configured](environment/magerc.sh) timeout -* Automatically run [database migrations](README.md#mage-database-setup) when the server starts, after connecting to MongoDB successfully -* Do not accept HTTP client connections until the database connection is successful and all migrations have run -* Added a [`docker-compose`](docker/docker-compose.yml) file and [`Dockerfile`](docker/server/Dockerfile) to [run MAGE](docker/README.md) as a Docker app -* Added environment [support](environment/env.js) and [placeholders](environment/magerc.sh) configuration for MongoDB [x509 authentication](https://docs.mongodb.com/v3.6/core/security-x.509/) and 2-way [SSL](https://docs.mongodb.com/v3.6/tutorial/configure-ssl/) between MAGE Server and MongoDB + +- Added GeoPackage layer support. Added server side XYZ urls to retrieve imagery from GeoPackages. The web client will use these URLs to display imagery tiles from a GeoPackage. Added server side url to retrieve vector tiles from feature GeoPackages. The web client will use these to display vector tiles (with the aid of a leaflet plugin). +- User account lock settings. Admins can now configure account lock/disable settings for local accounts. +- Replace local [environment](environment) NPM packages with a single Node module + \*\* No more manually deleting the local module from the `node_modules` directory for script changes +- Load values from [environment variables](README.md#mage-environment-settings) instead of only from the script + \*\* No more copying the modified environment script when [upgrading](README.md#upgrading-mage-server) the server +- Continuously attempt to connect to MongoDB when the server app starts, exiting after a [configured](environment/magerc.sh) timeout +- Automatically run [database migrations](README.md#mage-database-setup) when the server starts, after connecting to MongoDB successfully +- Do not accept HTTP client connections until the database connection is successful and all migrations have run +- Added a [`docker-compose`](docker/docker-compose.yml) file and [`Dockerfile`](docker/server/Dockerfile) to [run MAGE](docker/README.md) as a Docker app +- Added environment [support](environment/env.js) and [placeholders](environment/magerc.sh) configuration for MongoDB [x509 authentication](https://docs.mongodb.com/v3.6/core/security-x.509/) and 2-way [SSL](https://docs.mongodb.com/v3.6/tutorial/configure-ssl/) between MAGE Server and MongoDB ##### Bug Fixes -* Fixed secondary icon uploading for in event/form admin -* Show line breaks in disclaimer dialog. + +- Fixed secondary icon uploading for in event/form admin +- Show line breaks in disclaimer dialog. ## [5.1.3](https://github.com/ngageoint/mage-server/releases/tag/5.1.3) ##### Features ##### Bug Fixes -* Fixed secondary icon uploading for in event/form admin -* Don't allow EVENT_MANAGER_ROLE to change user roles -* Fix password strength meter on signup page -* Fix bug where confirmation modal does not disappear after deleting user -* Fix team filter on map page -* Initial save for banner/disclaimer settings fixed + +- Fixed secondary icon uploading for in event/form admin +- Don't allow EVENT_MANAGER_ROLE to change user roles +- Fix password strength meter on signup page +- Fix bug where confirmation modal does not disappear after deleting user +- Fix team filter on map page +- Initial save for banner/disclaimer settings fixed ## [5.1.2](https://github.com/ngageoint/mage-server/releases/tag/5.1.2) ##### Features -* mage-image plugin now uses mongo environment config. + +- mage-image plugin now uses mongo environment config. ##### Bug Fixes -* Fix a bug when non admin user queries for users or teams in an event. -* mage-image plugin now processes each attachment asynchronously. + +- Fix a bug when non admin user queries for users or teams in an event. +- mage-image plugin now processes each attachment asynchronously. ## [5.1.1](https://github.com/ngageoint/mage-server/releases/tag/5.1.1) ##### Features -* Added init.d script for mage application + +- Added init.d script for mage application ##### Bug Fixes -* Allow user with event access to update recent event -* Show default icon on web when creating an observation if no primary/secondary fields selected -* Add favicon in webpack build -* Don't show observation properties that are archived -* Don't submit observation properties that are archived + +- Allow user with event access to update recent event +- Show default icon on web when creating an observation if no primary/secondary fields selected +- Add favicon in webpack build +- Don't show observation properties that are archived +- Don't submit observation properties that are archived ## [5.1.0](https://github.com/ngageoint/mage-server/releases/tag/5.1.0) ##### Features -* Upgraded min/max nodejs version 6/8. -* Upgraded web from bower/grunt to npm/webpack. -* Updated admin icons and styles to properly cascade defaults. -* Added route to get teams including users for a specific event. Clients should use this as a performance boost to get only users that are part of an event. + +- Upgraded min/max nodejs version 6/8. +- Upgraded web from bower/grunt to npm/webpack. +- Updated admin icons and styles to properly cascade defaults. +- Added route to get teams including users for a specific event. Clients should use this as a performance boost to get only users that are part of an event. ##### Bug Fixes -* Fix bug when trying to set form line/polygon style. -* Add line and polygon support for observation export from news feed. -* Handle historic form import for pre 5.x forms. This will remove the timestamp and geometry fields from the imported form definition as + +- Fix bug when trying to set form line/polygon style. +- Add line and polygon support for observation export from news feed. +- Handle historic form import for pre 5.x forms. This will remove the timestamp and geometry fields from the imported form definition as those fields belong to the observation, not the form. ## [5.0.1](https://github.com/ngageoint/mage-server/releases/tag/5.0.1) ##### Features -* Added password strength meters to admin user password change and sign up page. + +- Added password strength meters to admin user password change and sign up page. ##### Bug Fixes -* Fix exporters (GeoJSON, KML, Shapefile, CSV) to work with multiple forms. -* Don't export archived fields. -* Fixed observation download to work with multiple forms. -* Changing the observation geometry type (point, line, polygon) in the middle of creating will not leave old shape on the map. -* Disable observation save while editing line and polygon until edit is complete. -* Fix race condition when loading devices and users in admin pages. -* Force reload devices and users every time a user goes to the admin page. -* Show required checkbox when editing historical 'type' fields. -* Fixed regex for password and password confirm match that was causing some like password to report a mismatch. -* Fixed a bug where new users password was checked against existing password, which of course didn't exist. -* Modify event projection query to contain acl and teamIds which allows for event CRUD permissions check. -* Don't allow event team to be removed from its event. + +- Fix exporters (GeoJSON, KML, Shapefile, CSV) to work with multiple forms. +- Don't export archived fields. +- Fixed observation download to work with multiple forms. +- Changing the observation geometry type (point, line, polygon) in the middle of creating will not leave old shape on the map. +- Disable observation save while editing line and polygon until edit is complete. +- Fix race condition when loading devices and users in admin pages. +- Force reload devices and users every time a user goes to the admin page. +- Show required checkbox when editing historical 'type' fields. +- Fixed regex for password and password confirm match that was causing some like password to report a mismatch. +- Fixed a bug where new users password was checked against existing password, which of course didn't exist. +- Modify event projection query to contain acl and teamIds which allows for event CRUD permissions check. +- Don't allow event team to be removed from its event. ## [5.0.0](https://github.com/ngageoint/mage-server/releases/tag/5.0.0) ##### Features -* Support for multiple forms per event -* Linestring and polygon observation support -* Upgraded npm dependencies to latest. -* Added map icon color and map icon initials columns to bulk user import. -* Trim leading/trailing white space from username when creating users. + +- Support for multiple forms per event +- Linestring and polygon observation support +- Upgraded npm dependencies to latest. +- Added map icon color and map icon initials columns to bulk user import. +- Trim leading/trailing white space from username when creating users. ##### Bug Fixes ## [4.5.2](https://github.com/ngageoint/mage-server/releases/tag/4.5.2) ##### Features -* Event and Team access control lists. + +- Event and Team access control lists. ##### Bug Fixes -* Base layers will never show on top of overlay layers. -* Last overlay layer clicked will be on top of other overlays. + +- Base layers will never show on top of overlay layers. +- Last overlay layer clicked will be on top of other overlays. ## [4.5.1](https://github.com/ngageoint/mage-server/releases/tag/4.5.1) ##### Features -* Added createdAt timestamp for observations + +- Added createdAt timestamp for observations ##### Bug Fixes ## [4.5.0](https://github.com/ngageoint/mage-server/releases/tag/4.5.0) ##### Features -* New routes to create observations. POST to /id will now generate the observation location (the id of the observation) - instead of the resource. After getting a server generated location for an observation resource, clients are + +- New routes to create observations. POST to /id will now generate the observation location (the id of the observation) + instead of the resource. After getting a server generated location for an observation resource, clients are responsible for send the observation data as a PUT (idempotent). This will prevent duplicate observations if the client loses the response from the POST. ##### Bug Fixes -* Multi select checkbox for dropdowns fixed -* Fix a bug with observations duplicating on the map when changing favorite/important filter. + +- Multi select checkbox for dropdowns fixed +- Fix a bug with observations duplicating on the map when changing favorite/important filter. ## [4.4.3](https://github.com/ngageoint/mage-server/releases/tag/4.4.3) ##### Features -* Admin can reorder select field options. -* Bulk user create. From 'Users' admin page click the 'Bulk Import' button to import a csv of users. -* When deleting a team, you can now delete all users that are part of that team. + +- Admin can reorder select field options. +- Bulk user create. From 'Users' admin page click the 'Bulk Import' button to import a csv of users. +- When deleting a team, you can now delete all users that are part of that team. ##### Bug Fixes @@ -566,84 +670,102 @@ from the Google CDN so the MAGE webapp does not need an Internet connection. ##### Features ##### Bug Fixes -* Fixed issue with viewing already created form fields for an event + +- Fixed issue with viewing already created form fields for an event ## [4.4.1](https://github.com/ngageoint/mage-server/releases/tag/4.4.1) ##### Features ##### Bug Fixes -* Fix migrations module to work with mongodb SSL connection + +- Fix migrations module to work with mongodb SSL connection ## [4.4.0](https://github.com/ngageoint/mage-server/releases/tag/4.4.0) #### This release include database migrations. -* Please run `npm run migrate` + +- Please run `npm run migrate` ##### Features -* Added observations favorites. Users can mark observations as a favorite, and can view other users favorites. -* Added important observations. Users with event edit permissions can mark observations as important. -* Added observation share/export. Share/export will package observation (including attachments) into a self contained html page. + +- Added observations favorites. Users can mark observations as a favorite, and can view other users favorites. +- Added important observations. Users with event edit permissions can mark observations as important. +- Added observation share/export. Share/export will package observation (including attachments) into a self contained html page. ##### Bug Fixes -* Changed default form type to textarea for textarea fields. This will enable users to add new lines to default. + +- Changed default form type to textarea for textarea fields. This will enable users to add new lines to default. ## [4.3.0](https://github.com/ngageoint/mage-server/releases/tag/4.3.0) ##### Features -* Multi select support for dropdown and user dropdown fields. -* Don't allow ordering of type, timestamp, geometry or variantField from the form editor. This will eliminate confusion + +- Multi select support for dropdown and user dropdown fields. +- Don't allow ordering of type, timestamp, geometry or variantField from the form editor. This will eliminate confusion since we always put those fields at the top of our clients. -* Update server configuration to allow for SSL communication between application and database. +- Update server configuration to allow for SSL communication between application and database. ##### Bug Fixes -* Added form import error handling on web client. -* Fixed feed layout height in Firefox. + +- Added form import error handling on web client. +- Fixed feed layout height in Firefox. ## [4.2.1](https://github.com/ngageoint/mage-server/releases/tag/4.2.1) + ##### Features -* OSM geocoder added. Search addresses from the MAGE map. On successful search the map will pan an zoom to that location. + +- OSM geocoder added. Search addresses from the MAGE map. On successful search the map will pan an zoom to that location. ##### Bug Fixes -* Only allow ISO8601 times for observation timestamps. Invalid time will result in a 400 response. -* Insert empty option for non required user fields in a form. This will allow to to select no user, or clear a currently selected user. -* Map should no longer hang on invalid XYZ/TMS data sources. + +- Only allow ISO8601 times for observation timestamps. Invalid time will result in a 400 response. +- Insert empty option for non required user fields in a form. This will allow to to select no user, or clear a currently selected user. +- Map should no longer hang on invalid XYZ/TMS data sources. ## [4.2.0](https://github.com/ngageoint/mage-server/releases/tag/4.2.0) + ##### Features -* New numberfield in event form -* Rework observation/people feed to show map and feed on smaller devices -* Allow user to upload avatar when creating an account -* Read user permissions (web) to determine observation edit capabilities + +- New numberfield in event form +- Rework observation/people feed to show map and feed on smaller devices +- Allow user to upload avatar when creating an account +- Read user permissions (web) to determine observation edit capabilities ##### Bug Fixes ## [4.1.1](https://github.com/ngageoint/mage-server/releases/tag/4.1.1) + ##### Features -* Added user agent and application version to device list on devices page. -* Maintain aspect ration for user avatars. + +- Added user agent and application version to device list on devices page. +- Maintain aspect ration for user avatars. ##### Bug Fixes -* User agent and application version now parsed and saved w/ device on login. -* Fixed flexbox layout issues for map popups in IE 11. -* Fixed observation edit not allowing save in some situations. + +- User agent and application version now parsed and saved w/ device on login. +- Fixed flexbox layout issues for map popups in IE 11. +- Fixed observation edit not allowing save in some situations. ## [4.1.0](https://github.com/ngageoint/mage-server/releases/tag/4.1.0) + ##### Features -* 'Event Users' dropdown in form builder. This dropdown is dynamically populated with all users in the event. -* Create your own user icon from the user edit page. -* Added environment module to support local and Cloud Foundry deployments. -* When creating a new MAGE server the initial user and device are now creating through the web interface, rather then by a database patch. -* Added some Excel specific fields to CSV export data. + +- 'Event Users' dropdown in form builder. This dropdown is dynamically populated with all users in the event. +- Create your own user icon from the user edit page. +- Added environment module to support local and Cloud Foundry deployments. +- When creating a new MAGE server the initial user and device are now creating through the web interface, rather then by a database patch. +- Added some Excel specific fields to CSV export data. ##### Bug Fixes -* Fixed a bug where canceling an observation edit did not also clear attachment delete flag. This made it look like the attachment may still have been deleted after clicking cancel. + +- Fixed a bug where canceling an observation edit did not also clear attachment delete flag. This made it look like the attachment may still have been deleted after clicking cancel. ## [4.0.1](https://github.com/ngageoint/mage-server/releases/tag/4.0.1) -* You can now add users directly to an event -* Events can be marked as complete. Complete events will not be returned for the default events route. +- You can now add users directly to an event +- Events can be marked as complete. Complete events will not be returned for the default events route. ## [4.0.0](https://github.com/ngageoint/mage-server/releases/tag/4.0.0) -* Google oauth support +- Google oauth support From ae3d0c94c11255f4f6891d0e3a4f1ab72cdeb333 Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Fri, 1 Sep 2023 11:00:04 -0500 Subject: [PATCH 10/13] migration 030-add-read-system-info-permissions --- .../authorization/entities.permissions.ts | 26 ++++++++--------- .../030-add-read-system-info-permissions.js | 29 +++++++++++++++++++ .../permissions.systemInfo.test.ts | 9 ++---- 3 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 service/src/migrations/030-add-read-system-info-permissions.js diff --git a/service/src/entities/authorization/entities.permissions.ts b/service/src/entities/authorization/entities.permissions.ts index 03cb97acd..6f66d013a 100644 --- a/service/src/entities/authorization/entities.permissions.ts +++ b/service/src/entities/authorization/entities.permissions.ts @@ -95,19 +95,19 @@ export enum StaticIconPermission { } export const allPermissions = Object.freeze({ - ...DevicePermission, - ...UsersPermission, - ...RolePermission, - ...MageEventPermission, - ...LayerPermission, - ...ObservationPermission, - ...LocationPermission, - ...TeamPermission, - ...SettingPermission, - ...FeedsPermission, - ...StaticIconPermission, - ...SystemInfoPermission - }); + ...DevicePermission, + ...UsersPermission, + ...RolePermission, + ...MageEventPermission, + ...LayerPermission, + ...ObservationPermission, + ...LocationPermission, + ...TeamPermission, + ...SettingPermission, + ...FeedsPermission, + ...StaticIconPermission, + ...SystemInfoPermission +}); export type AnyPermission = | DevicePermission diff --git a/service/src/migrations/030-add-read-system-info-permissions.js b/service/src/migrations/030-add-read-system-info-permissions.js new file mode 100644 index 000000000..4f19e6af5 --- /dev/null +++ b/service/src/migrations/030-add-read-system-info-permissions.js @@ -0,0 +1,29 @@ +const mongoose = require('mongoose'); +const RoleModel = mongoose.model('Role'); + +exports.id = 'add-read-system-info-permission'; + +exports.up = function(done) { + this.log('adding READ_SYSTEM_INFO permission to ADMIN_ROLE ...'); + + // Use $addToSet to ensure the permission is only added if it doesn't exist + RoleModel.updateOne( + { name: 'ADMIN_ROLE' }, + { $addToSet: { permissions: 'READ_SYSTEM_INFO' } }, + function(err) { + done(err); + } + ); +}; + +exports.down = function(done) { + this.log('removing READ_SYSTEM_INFO permission from ADMIN_ROLE ...'); + + RoleModel.updateOne( + { name: 'ADMIN_ROLE' }, + { $pull: { permissions: 'READ_SYSTEM_INFO' } }, + function(err) { + done(err); + } + ); +}; diff --git a/service/test/permissions/permissions.systemInfo.test.ts b/service/test/permissions/permissions.systemInfo.test.ts index 00b3bc438..6290f90b2 100644 --- a/service/test/permissions/permissions.systemInfo.test.ts +++ b/service/test/permissions/permissions.systemInfo.test.ts @@ -35,9 +35,7 @@ describe('system info role-based permission service', function() { expect(denied?.code).to.equal(ErrPermissionDenied); expect(denied?.data.subject).to.equal('neverever'); - expect(denied?.data.permission).to.equal( - SystemInfoPermission.READ_SYSTEM_INFO - ); + expect(denied?.data.permission).to.equal(SystemInfoPermission.READ_SYSTEM_INFO); expect(denied?.data.object).to.equal('SystemInfo'); }); @@ -48,10 +46,7 @@ describe('system info role-based permission service', function() { return ({ username: 'haspermission', roleId: { - permissions: [ - allPermissions.READ_SYSTEM_INFO, - SystemInfoPermission.READ_SYSTEM_INFO - ] + permissions: [ SystemInfoPermission.READ_SYSTEM_INFO ] } } as unknown) as UserWithRole; }, From 1234665cb7b6b8f7174e25d60c6d1855df6aa93b Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Fri, 1 Sep 2023 11:36:18 -0500 Subject: [PATCH 11/13] fixed 003-create-admin migration --- service/src/migrations/003-create-admin-role.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/src/migrations/003-create-admin-role.js b/service/src/migrations/003-create-admin-role.js index 19638b080..0992cb786 100644 --- a/service/src/migrations/003-create-admin-role.js +++ b/service/src/migrations/003-create-admin-role.js @@ -11,7 +11,7 @@ exports.up = function(done) { 'CREATE_USER', 'READ_USER', 'UPDATE_USER', 'DELETE_USER', 'CREATE_ROLE', 'READ_ROLE', 'UPDATE_ROLE', 'DELETE_ROLE', 'CREATE_EVENT', 'READ_EVENT_ALL', 'UPDATE_EVENT', 'DELETE_EVENT', - 'CREATE_LAYER', 'READ_LAYER_ALL', 'READ_SYSTEM_INFO','UPDATE_LAYER', 'DELETE_LAYER', + 'CREATE_LAYER', 'READ_LAYER_ALL', ,'UPDATE_LAYER', 'DELETE_LAYER', 'CREATE_OBSERVATION', 'READ_OBSERVATION_ALL', 'UPDATE_OBSERVATION_ALL', 'DELETE_OBSERVATION', 'CREATE_LOCATION', 'READ_LOCATION_ALL', 'UPDATE_LOCATION_ALL', 'DELETE_LOCATION', 'CREATE_TEAM', 'READ_TEAM', 'UPDATE_TEAM', 'DELETE_TEAM']; From 54f2c4a89214bb6e360c8843e273781a148c9472 Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Fri, 1 Sep 2023 14:24:47 -0500 Subject: [PATCH 12/13] removed extra comma migration 003 migration file --- service/src/migrations/003-create-admin-role.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/src/migrations/003-create-admin-role.js b/service/src/migrations/003-create-admin-role.js index 0992cb786..ae4490b68 100644 --- a/service/src/migrations/003-create-admin-role.js +++ b/service/src/migrations/003-create-admin-role.js @@ -11,7 +11,7 @@ exports.up = function(done) { 'CREATE_USER', 'READ_USER', 'UPDATE_USER', 'DELETE_USER', 'CREATE_ROLE', 'READ_ROLE', 'UPDATE_ROLE', 'DELETE_ROLE', 'CREATE_EVENT', 'READ_EVENT_ALL', 'UPDATE_EVENT', 'DELETE_EVENT', - 'CREATE_LAYER', 'READ_LAYER_ALL', ,'UPDATE_LAYER', 'DELETE_LAYER', + 'CREATE_LAYER', 'READ_LAYER_ALL', 'UPDATE_LAYER', 'DELETE_LAYER', 'CREATE_OBSERVATION', 'READ_OBSERVATION_ALL', 'UPDATE_OBSERVATION_ALL', 'DELETE_OBSERVATION', 'CREATE_LOCATION', 'READ_LOCATION_ALL', 'UPDATE_LOCATION_ALL', 'DELETE_LOCATION', 'CREATE_TEAM', 'READ_TEAM', 'UPDATE_TEAM', 'DELETE_TEAM']; From 09255fc35a30a52db1037c178cab0f2a0d52eb66 Mon Sep 17 00:00:00 2001 From: kimura-developer <136853071+kimura-developer@users.noreply.github.com> Date: Fri, 1 Sep 2023 14:57:51 -0500 Subject: [PATCH 13/13] test fix --- .../permissions/permissions.systemInfo.test.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/service/test/permissions/permissions.systemInfo.test.ts b/service/test/permissions/permissions.systemInfo.test.ts index 6290f90b2..27b72c6e0 100644 --- a/service/test/permissions/permissions.systemInfo.test.ts +++ b/service/test/permissions/permissions.systemInfo.test.ts @@ -35,8 +35,10 @@ describe('system info role-based permission service', function() { expect(denied?.code).to.equal(ErrPermissionDenied); expect(denied?.data.subject).to.equal('neverever'); - expect(denied?.data.permission).to.equal(SystemInfoPermission.READ_SYSTEM_INFO); - expect(denied?.data.object).to.equal('SystemInfo'); + expect(denied?.data.permission).to.equal( + SystemInfoPermission.READ_SYSTEM_INFO + ); + expect(denied?.data.object).to.be.null; // Expecting no object in the denied data as per the static icon example. }); it('allows read permission if user has read system info permission', async function() { @@ -44,9 +46,9 @@ describe('system info role-based permission service', function() { requestToken: Symbol(), requestingPrincipal() { return ({ - username: 'haspermission', + username: 'canread', roleId: { - permissions: [ SystemInfoPermission.READ_SYSTEM_INFO ] + permissions: [SystemInfoPermission.READ_SYSTEM_INFO] } } as unknown) as UserWithRole; }, @@ -57,6 +59,10 @@ describe('system info role-based permission service', function() { const denied = await permissions.ensureReadSystemInfoPermission(ctx); - expect(denied).to.be.null; + expect(denied).to.be.null; // No permission denied error should be returned. }); + + // If there's any other special permission behavior specific to SystemInfo, + // you can model another test case here similar to the static icon's "allows get permission always". }); +