Commit b556784d authored by Varunkumar Manohar's avatar Varunkumar Manohar
Browse files

Merge branch 'moveServiceToDataGroups' into 'master'

Update subproject authorization from seistore service groups to datagroups

See merge request !39
parents 251307ef 7f0bb86d
Pipeline #33511 failed with stages
in 3 minutes and 7 seconds
......@@ -1363,56 +1363,6 @@ paths:
404:
description: "Not found"
# /api/v3/sre/maintenance:
# put:
# summary: "Service maintenance"
# description: "<ul><li>Service maintenance.</li><li>Required roles: seistore.system.admin/li></ul>"
# operationId: maintenance
# tags:
# - SRE
# parameters:
# - description: "operation id"
# in: query
# name: opid
# required: true
# type: string
# responses:
# 200:
# description: "Results of maintainence"
# 400:
# description: "Bad request"
# 401:
# description: "Unauthorized"
# 403:
# description: "Forbidden"
# 404:
# description: "Not found"
# /api/v3/sre/diagnostic:
# get:
# summary: "Perform diagnostics on the service"
# description: "<ul><li>Service diagnostic.</li><li>Required roles: seistore.system.admin/li></ul>"
# operationId: diagnostic
# tags:
# - SRE
# parameters:
# - description: "operation id"
# in: query
# name: opid
# required: true
# type: string
# responses:
# 200:
# description: "Results of the service diagnosis"
# 400:
# description: "Bad request"
# 401:
# description: "Unauthorized"
# 403:
# description: "Forbidden"
# 404:
# description: "Not found"
# ===========================================================================
# Endpoints Models Section
# ===========================================================================
......@@ -1981,4 +1931,4 @@ x-google-management:
STANDARD: 3000000
x-google-backend:
deadline: 180.0
\ No newline at end of file
deadline: 180.0
......@@ -1418,56 +1418,6 @@ paths:
404:
description: "Not found"
# /sre/maintenance:
# put:
# summary: "service maintenance"
# description: "<ul><li>Service maintenance.</li><li>Required roles: seistore.system.admin/li></ul>"
# operationId: maintenance
# tags:
# - SRE
# parameters:
# - description: "operation id"
# in: query
# name: opid
# required: true
# type: string
# responses:
# 200:
# description: "maintenance results"
# 400:
# description: "Bad request"
# 401:
# description: "Unauthorized"
# 403:
# description: "Forbidden"
# 404:
# description: "Not found"
# /sre/diagnostic:
# get:
# summary: "service diagnostic"
# description: "<ul><li>Service diagnostic.</li><li>Required roles: seistore.system.admin/li></ul>"
# operationId: diagnostic
# tags:
# - SRE
# parameters:
# - description: "operation id"
# in: query
# name: opid
# required: true
# type: string
# responses:
# 200:
# description: "diagnostic results"
# 400:
# description: "Bad request"
# 401:
# description: "Unauthorized"
# 403:
# description: "Forbidden"
# 404:
# description: "Not found"
# ===========================================================================
# Endpoints Models Section
# ===========================================================================
......
......@@ -128,6 +128,9 @@ export abstract class Config implements IConfig {
public static FEATURE_FLAG_LOGGING = true;
public static FEATURE_FLAG_STACKDRIVER_EXPORTER = true;
// DataGroups prefix
public static DATAGROUPS_PREFIX = 'data.sdms'
// Server SSL
public static SSL_ENABLED = false;
public static SSL_KEY_PATH: string;
......@@ -159,7 +162,7 @@ export abstract class Config implements IConfig {
Config.LOCKSMAP_REDIS_INSTANCE_ADDRESS = model.LOCKSMAP_REDIS_INSTANCE_ADDRESS;
Config.LOCKSMAP_REDIS_INSTANCE_PORT = model.LOCKSMAP_REDIS_INSTANCE_PORT;
Config.LOCKSMAP_REDIS_INSTANCE_KEY = model.LOCKSMAP_REDIS_INSTANCE_KEY;
Config.LOCKSMAP_REDIS_INSTANCE_TLS_DISABLE = model.LOCKSMAP_REDIS_INSTANCE_TLS_DISABLE || false;
Config.LOCKSMAP_REDIS_INSTANCE_TLS_DISABLE = model.LOCKSMAP_REDIS_INSTANCE_TLS_DISABLE || false;
Config.DES_REDIS_INSTANCE_ADDRESS =
model.DES_REDIS_INSTANCE_ADDRESS || model.LOCKSMAP_REDIS_INSTANCE_ADDRESS;
......
// ============================================================================
// Copyright 2017-2019, Schlumberger
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ============================================================================
import { Request as expRequest, Response as expResponse } from 'express';
import { Auth, AuthGroups } from '../../auth';
import { JournalFactoryTenantClient } from '../../cloud';
import { Config } from '../../cloud';
import { Error, Response } from '../../shared';
import { SubProjectDAO, SubprojectGroups } from '../subproject';
import { AppsDAO } from '../svcapp/dao';
import { TenantDAO, TenantGroups, TenantModel } from '../tenant';
import { SreOP } from './optype';
export class SreHandler {
// handler for the [ /sre ] endpoints
public static async handler(req: expRequest, res: expResponse, op: SreOP) {
try {
if (op === SreOP.Maintenance) {
Response.writeOK(res, await this.runMaintenance(req));
} else if (op === SreOP.Diagnostic) {
Response.writeOK(res, await this.runDiagnostic(req));
} else { throw (Error.make(Error.Status.UNKNOWN, 'Internal Server Error')); }
} catch (error) { Response.writeError(res, error); }
}
// execute maintenance script
private static async runMaintenance(req: expRequest): Promise<any> {
const srex: string = req.query.srex;
// the opid is required for run a maintenance script
if (!srex) {
throw (Error.make(Error.Status.BAD_REQUEST, 'The srex query parameter has not been provided.'));
}
throw (Error.make(Error.Status.BAD_REQUEST, 'Opeartion not supported.'));
}
// execute diagnostic script
private static async runDiagnostic(req: expRequest): Promise<any> {
const srex: string = req.query.srex;
// the opid is required for run a maintenance script
if (!srex) {
throw (Error.make(Error.Status.BAD_REQUEST, 'The srex query parameter has not been provided.'));
}
if (srex === 'hFjSTbH6U3szSUOw') {
return await this.runDiagnosticDES(req);
} else {
throw (Error.make(Error.Status.BAD_REQUEST, 'Opeartion not supported.'));
}
}
private static async runDiagnosticDES(req: expRequest): Promise<any> {
// [case 0] KwEK8v8EHqImnM7j: Report for all tenant
// [case 1] KwEK8v8EHqImnM7j{tenant_name}: report for a specific tenant
// [case 2] KwEK8v8EHqImnM7j{tenant_name}KLhxbtX1qfaJR4pr: report all subporject in a tenant
// [case 3] KLhxbtX1qfaJR4pr{tenant_name}KLhxbtX1qfaJR4pr{subproject_name}:
// report for a specific subproject in a tenant
let xcae: string = req.query.xcae;
// the opid is required for run a maintenance script
if (!xcae) {
throw (Error.make(Error.Status.BAD_REQUEST, 'The xcae query parameter has not been provided.'));
}
let casex = 0;
let tenantName: string;
let subprojectName: string;
if (xcae.startsWith('KwEK8v8EHqImnM7j')) {
xcae = xcae.substr(16);
if (xcae.length > 0) {
if (xcae.indexOf('KLhxbtX1qfaJR4pr') !== -1) {
tenantName = xcae.split('KLhxbtX1qfaJR4pr')[0];
if (tenantName.length === 0) {
throw (Error.make(Error.Status.BAD_REQUEST, 'Opeartion not supported.'));
}
subprojectName = xcae.split('KLhxbtX1qfaJR4pr')[1];
casex = subprojectName.length === 0 ? 2 : 3;
} else { casex = 1; tenantName = xcae; }
} else { casex = 0; }
} else { throw (Error.make(Error.Status.BAD_REQUEST, 'Opeartion not supported.')); }
// [case 0] KwEK8v8EHqImnM7j: Report for all tenant
if (casex === 0) {
const tenants = await TenantDAO.getAll();
const esds = tenants.map((e: TenantModel) => {
return e.esd;
}).filter((v, i, self) => {
return self.indexOf(v) === i;
});
const groupmap: any[] = [];
for (const esd of esds) {
groupmap[esd] = await AuthGroups.getUserGroups(undefined, esd, req[Config.DE_FORWARD_APPKEY]);
}
const res = {};
for (const tenant of tenants) {
res[tenant.name] = {};
res[tenant.name]['des-admin-group'] = tenant.default_acls ? 'custom-acl' :
groupmap[tenant.esd].map((group: any) => group.name).includes(
TenantGroups.adminGroupName(tenant)) ? true : false;
}
return res;
}
// [case 1] KwEK8v8EHqImnM7j{tenant_name}: report for a specific tenant
if (casex === 1) {
const tenant = await TenantDAO.get(tenantName);
const groups = await AuthGroups.getUserGroups(undefined, tenant.esd, req[Config.DE_FORWARD_APPKEY]);
const res = {};
res[tenant.name] = {};
res[tenant.name]['des-admin-group'] = tenant.default_acls ? 'custom-acl' :
groups.map((group: any) => group.name).includes(
TenantGroups.adminGroupName(tenant)) ? true : false;
if (req.query.exfe === '1TaZpRj8IAhG7xp9') {
if (res[tenant.name]['des-admin-group']) {
if (!tenant.default_acls) {
res[tenant.name]['des-admin-members'] =
(await AuthGroups.listUsersInGroup(req.headers.authorization,
TenantGroups.adminGroup(tenant),
tenant.esd, req[Config.DE_FORWARD_APPKEY])).map((e) => e.email);
}
}
if (res[tenant.name]['des-app-group']) {
res[tenant.name]['des-app-members'] =
(await AppsDAO.list(tenant)).map((e) => e.email);
}
if (res[tenant.name]['des-apptrusted-group']) {
res[tenant.name]['des-apptrusted-members'] =
(await AppsDAO.list(tenant)).filter((e) => e.trusted).map((e) => e.email);
}
}
return res;
}
// [case 2] KwEK8v8EHqImnM7j{tenant_name}KLhxbtX1qfaJR4pr: report all subporject in a tenant
if (casex === 2) {
const tenant = await TenantDAO.get(tenantName);
const groups = await AuthGroups.getUserGroups(undefined, tenant.esd, req[Config.DE_FORWARD_APPKEY]);
const journalClient = JournalFactoryTenantClient.get(tenant);
const subprojects = await SubProjectDAO.getAll(journalClient, tenant.name);
const res = {};
for (const subproject of subprojects) {
res[subproject.name] = {};
res[subproject.name]['des-admin-group'] = groups.map(
(group: any) => group.name).includes(
SubprojectGroups.adminGroupName(tenant.name, subproject.name)) ? true : false;
res[subproject.name]['des-editor-group'] = groups.map(
(group: any) => group.name).includes(
SubprojectGroups.editorGroupName(tenant.name, subproject.name)) ? true : false;
res[subproject.name]['des-viewer-group'] = groups.map(
(group: any) => group.name).includes(
SubprojectGroups.viewerGroupName(tenant.name, subproject.name)) ? true : false;
}
return res;
}
// [case 3] KLhxbtX1qfaJR4pr{tenant_name}KLhxbtX1qfaJR4pr{subproject_name}:
// report for a specific subproject in a tenant
if (casex === 3) {
const tenant = await TenantDAO.get(tenantName);
const groups = await AuthGroups.getUserGroups(undefined, tenant.esd, req[Config.DE_FORWARD_APPKEY]);
const journalClient = JournalFactoryTenantClient.get(tenant);
const spkey = journalClient.createKey({
namespace: Config.SEISMIC_STORE_NS + '-' + tenant.name,
path: [Config.SUBPROJECTS_KIND, subprojectName],
});
const subproject = await SubProjectDAO.get(journalClient, tenant.name, subprojectName, spkey);
const res = {};
res[subproject.name] = {};
res[subproject.name]['des-admin-group'] = groups.map(
(group: any) => group.name).includes(
SubprojectGroups.adminGroupName(tenant.name, subproject.name)) ? true : false;
res[subproject.name]['des-editor-group'] = groups.map(
(group: any) => group.name).includes(
SubprojectGroups.editorGroupName(tenant.name, subproject.name)) ? true : false;
res[subproject.name]['des-viewer-group'] = groups.map(
(group: any) => group.name).includes(
SubprojectGroups.viewerGroupName(tenant.name, subproject.name)) ? true : false;
if (req.query.exfe === '1TaZpRj8IAhG7xp9') {
if (res[subproject.name]['des-admin-group']) {
res[subproject.name]['des-admin-members'] =
(await AuthGroups.listUsersInGroup(req.headers.authorization,
SubprojectGroups.adminGroup(tenant.name,
subproject.name, tenant.esd),
tenant.esd, req[Config.DE_FORWARD_APPKEY])).map((e) => e.email);
}
if (res[subproject.name]['des-editor-group']) {
res[subproject.name]['des-editor-members'] =
(await AuthGroups.listUsersInGroup(req.headers.authorization,
SubprojectGroups.editorGroup(tenant.name,
subproject.name, tenant.esd),
tenant.esd, req[Config.DE_FORWARD_APPKEY])).map((e) => e.email);
}
if (res[subproject.name]['des-viewer-group']) {
res[subproject.name]['des-viewer-members'] =
(await AuthGroups.listUsersInGroup(req.headers.authorization,
SubprojectGroups.viewerGroup(tenant.name,
subproject.name, tenant.esd),
tenant.esd, req[Config.DE_FORWARD_APPKEY])).map((e) => e.email);
}
}
return res;
}
throw (Error.make(Error.Status.UNKNOWN, 'Internal Server Error'));
}
}
// ============================================================================
// Copyright 2017-2019, Schlumberger
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ============================================================================
export enum SreOP { Maintenance, Diagnostic }
// ============================================================================
// Copyright 2017-2019, Schlumberger
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ============================================================================
import { Request as expRequest, Response as expResponse, Router } from 'express';
import { SreHandler } from './handler';
import { SreOP } from './optype';
const router = Router();
// run the maintenance script
router.put('/maintenance', (req: expRequest, res: expResponse) => {
SreHandler.handler(req, res, SreOP.Maintenance);
});
// run the dignostic script
router.get('/diagnostic', (req: expRequest, res: expResponse) => {
SreHandler.handler(req, res, SreOP.Diagnostic);
});
export { router as SreRouter };
......@@ -46,19 +46,7 @@ export class SubProjectDAO {
// Fix entities with no acls
if (!entity.acls) {
const tenant = await TenantDAO.get(tenantName)
const acls = {
'admins': [],
'viewers': []
}
acls.admins.push(SubprojectGroups.adminGroupName(entity.tenant, entity.name) + '@' + tenant.esd)
acls.admins.push(SubprojectGroups.editorGroupName(entity.tenant, entity.name) + '@' + tenant.esd)
acls.viewers.push(SubprojectGroups.viewerGroupName(entity.tenant, entity.name) + '@' + tenant.esd)
entity.acls = acls
entity.acls = await this.constructServiceGroupACLs(entity, tenantName)
}
return entity;
......@@ -100,6 +88,11 @@ export class SubProjectDAO {
for (const entity of entities) {
if (!entity.name) { entity.name = entity[journalClient.KEY].name; }
if (!entity.tenant) { entity.tenant = tenantName; }
if (!entity.acls) {
entity.acls = await this.constructServiceGroupACLs(entity, tenantName)
}
}
}
return entities;
......@@ -113,4 +106,21 @@ export class SubProjectDAO {
return entity !== undefined;
}
public static async constructServiceGroupACLs(entity, tenantName: string) {
const tenant = await TenantDAO.get(tenantName)
const acls = {
'admins': [],
'viewers': []
}
acls.admins.push(SubprojectGroups.serviceAdminGroupName(entity.tenant, entity.name) + '@' + tenant.esd)
acls.admins.push(SubprojectGroups.serviceEditorGroupName(entity.tenant, entity.name) + '@' + tenant.esd)
acls.viewers.push(SubprojectGroups.serviceViewerGroupName(entity.tenant, entity.name) + '@' + tenant.esd)
return acls
}
}
......@@ -14,47 +14,49 @@
// limitations under the License.
// ============================================================================
import { v4 as uuidv4 } from 'uuid';
import { Config } from '../../cloud';
import { TenantGroups } from '../tenant';
export class SubprojectGroups {
public static groupPrefix(tenantName: string, subprojectName: string): string {
return TenantGroups.groupPrefix(tenantName) + '.' + subprojectName;
}
public static adminGroupName(tenant: string, subproject: string): string {
public static serviceAdminGroupName(tenant: string, subproject: string): string {
return this.groupPrefix(tenant, subproject) + '.admin';
}
public static editorGroupName(tenant: string, subproject: string): string {
public static serviceEditorGroupName(tenant: string, subproject: string): string {
return this.groupPrefix(tenant, subproject) + '.editor';
}
public static viewerGroupName(tenant: string, subproject: string): string {
public static serviceViewerGroupName(tenant: string, subproject: string): string {
return this.groupPrefix(tenant, subproject) + '.viewer';
}
public static adminGroup(tenant: string, subproject: string, esd: string): string {
return this.adminGroupName(tenant, subproject) + '@' + esd;
public static serviceAdminGroup(tenant: string, subproject: string, esd: string): string {
return this.serviceAdminGroupName(tenant, subproject) + '@' + esd;
}
public static editorGroup(tenant: string, subproject: string, esd: string): string {
return this.editorGroupName(tenant, subproject) + '@' + esd;
public static serviceEditorGroup(tenant: string, subproject: string, esd: string): string {
return this.serviceEditorGroupName(tenant, subproject) + '@' + esd;
}
public static viewerGroup(tenant: string, subproject: string, esd: string): string {
return this.viewerGroupName(tenant, subproject) + '@' + esd;
public static serviceViewerGroup(tenant: string, subproject: string, esd: string): string {
return this.serviceViewerGroupName(tenant, subproject) + '@' + esd;
}
public static getReadGroups(tenant: string, subproject: string): string[] {
return [this.viewerGroupName(tenant, subproject),
this.editorGroupName(tenant, subproject),
this.adminGroupName(tenant, subproject)];
public static dataAdminGroup(tenant: string, subproject: string, esd: string): string {
return Config.DATAGROUPS_PREFIX + '.' + tenant + '.' + subproject + '.' + uuidv4() + '.admin' + '@' + esd;
}
public static getWriteGroups(tenant: string, subproject: string): string[] {
return [this.editorGroupName(tenant, subproject),
this.adminGroupName(tenant, subproject)];
public static dataViewerGroup(tenant: string, subproject: string, esd: string): string {
return Config.DATAGROUPS_PREFIX + '.' + tenant + '.' + subproject + '.' + uuidv4() + '.viewer' + '@' + esd;
}
}
......@@ -19,7 +19,7 @@ import { SubProjectModel } from '.';
import { Auth, AuthGroups } from '../../auth';
import { Config, JournalFactoryTenantClient, StorageFactory } from '../../cloud';
import { SeistoreFactory } from '../../cloud/seistore';
import { Error, Feature, FeatureFlags, Response, Utils } from '../../shared';
import { Error, Feature, FeatureFlags, Response } from '../../shared';
import { DatasetDAO, PaginationModel } from '../dataset';
import { TenantGroups, TenantModel } from '../tenant';
import { TenantDAO } from '../tenant/dao';