"use strict";
/*
 *  Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *  SPDX-License-Identifier: Apache-2.0
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataSetService = void 0;
const swb_app_1 = require("@aws/swb-app");
const workbench_core_authorization_1 = require("@aws/workbench-core-authorization");
const workbench_core_base_1 = require("@aws/workbench-core-base");
const roleUtils_1 = require("../utils/roleUtils");
const timeToLiveSeconds = 60 * 2; // 2 min
class DataSetService {
    constructor(dataSetStoragePlugin, workbenchDataSetService, dataSetAuthService, databaseService, dynamicAuthService) {
        this._workbenchDataSetService = workbenchDataSetService;
        this.storagePlugin = dataSetStoragePlugin;
        this._dataSetsAuthService = dataSetAuthService;
        this._databaseService = databaseService;
        this._dynamicAuthService = dynamicAuthService;
    }
    async removeDataSet(dataSetId, authenticatedUser) {
        const dataset = await this.getDataSet(dataSetId, authenticatedUser);
        const associatedProjects = await this._associatedProjects(dataset);
        if (associatedProjects.length > 0) {
            throw new swb_app_1.ConflictError(`DataSet ${dataSetId} cannot be removed because it is still associated with roles in the provided project(s)`);
        }
        await this._workbenchDataSetService.removeDataSet(dataSetId, () => {
            return Promise.resolve();
        }, authenticatedUser);
        const projectId = dataset.owner.split('#')[0];
        const projectAdmin = (0, roleUtils_1.getProjectAdminRole)(projectId);
        await this._removeAuthZPermissionsForDataset(authenticatedUser, "SWB_DATASET" /* SwbAuthZSubject.SWB_DATASET */, dataSetId, [projectAdmin], ['READ', 'UPDATE', 'DELETE']);
        await this._removeAuthZPermissionsForDataset(authenticatedUser, "SWB_DATASET_ACCESS_LEVEL" /* SwbAuthZSubject.SWB_DATASET_ACCESS_LEVEL */, `${dataSetId}`, [projectAdmin], ['READ', 'UPDATE', 'DELETE']);
        const researcher = (0, roleUtils_1.getResearcherRole)(projectId);
        await this._removeAuthZPermissionsForDataset(authenticatedUser, "SWB_DATASET" /* SwbAuthZSubject.SWB_DATASET */, dataSetId, [researcher], ['READ']);
        await this._removeAuthZPermissionsForDataset(authenticatedUser, "SWB_DATASET_UPLOAD" /* SwbAuthZSubject.SWB_DATASET_UPLOAD */, dataSetId, [projectAdmin, researcher], ['READ']);
    }
    async _associatedProjects(dataset) {
        const permissions = await this._dynamicAuthService.getIdentityPermissionsBySubject({
            subjectId: dataset.id,
            subjectType: "SWB_DATASET" /* SwbAuthZSubject.SWB_DATASET */
        });
        const projectIds = permissions.data.identityPermissions
            .filter((permission) => permission.identityType === 'GROUP')
            .filter((permission) => permission.identityId.split('#')[0] !== dataset.owner.split('#')[0])
            .map((permission) => `'${permission.identityId.split('#')[0]}'`);
        return Array.from(new Set(projectIds));
    }
    addDataSetExternalEndpoint(request) {
        return this._workbenchDataSetService.addDataSetExternalEndpointForGroup(Object.assign(Object.assign({}, request), { storageProvider: this.storagePlugin }));
    }
    getDataSet(dataSetId, authenticatedUser) {
        return this._workbenchDataSetService.getDataSet(dataSetId, authenticatedUser);
    }
    importDataSet(request) {
        return this._workbenchDataSetService.importDataSet(request);
    }
    listDataSets(user, pageSize, paginationToken) {
        return this._workbenchDataSetService.listDataSets(user, pageSize, paginationToken);
    }
    async listDataSetsForProject(projectId, user, pageSize, paginationToken) {
        let projectDatasets = [];
        if (user.roles.includes('ITAdmin')) {
            return {
                data: [],
                paginationToken: undefined
            };
        }
        let lastPaginationToken = paginationToken;
        do {
            const dataSetsOnPageResponse = await this._workbenchDataSetService.listDataSets(user, pageSize, lastPaginationToken);
            projectDatasets = projectDatasets.concat(dataSetsOnPageResponse.data.filter((dataset) => { var _a; return ((_a = dataset.owner) === null || _a === void 0 ? void 0 : _a.split('#')[0]) === projectId; }));
            lastPaginationToken = dataSetsOnPageResponse.paginationToken;
        } while (projectDatasets.length < pageSize && lastPaginationToken);
        if (projectDatasets.length > pageSize) {
            projectDatasets = projectDatasets.slice(0, pageSize);
            lastPaginationToken = this._workbenchDataSetService.getPaginationToken(projectDatasets[pageSize - 1].id);
        }
        return {
            data: projectDatasets,
            paginationToken: lastPaginationToken
        };
    }
    async listDataSetAccessPermissions(request) {
        const response = await this._workbenchDataSetService.getAllDataSetAccessPermissions(request.dataSetId, request.authenticatedUser, request.paginationToken, request.pageSize);
        return swb_app_1.PermissionsResponseParser.parse(response);
    }
    async provisionDataSet(projectId, request) {
        //add permissions in AuthZ for user to read, write, update, delete, and update read/write permissions
        const projectAdmin = (0, roleUtils_1.getProjectAdminRole)(projectId);
        const projectResearcher = (0, roleUtils_1.getResearcherRole)(projectId);
        request.ownerType = 'GROUP';
        request.owner = projectAdmin;
        request.permissions = [
            {
                identity: projectAdmin,
                identityType: 'GROUP',
                accessLevel: 'read-write'
            },
            {
                identity: projectResearcher,
                identityType: 'GROUP',
                accessLevel: 'read-write'
            }
        ];
        const dataset = await this._workbenchDataSetService.provisionDataSet(request);
        await this._addAuthZPermissionsForDataset(request.authenticatedUser, "SWB_DATASET" /* SwbAuthZSubject.SWB_DATASET */, dataset.id, [projectAdmin], ['READ', 'UPDATE', 'DELETE'], { projectId: { $eq: projectId } });
        await this._addAuthZPermissionsForDataset(request.authenticatedUser, "SWB_DATASET" /* SwbAuthZSubject.SWB_DATASET */, dataset.id, [projectResearcher], ['READ'], { projectId: { $eq: projectId } });
        await this._addAuthZPermissionsForDataset(request.authenticatedUser, "SWB_DATASET_ACCESS_LEVEL" /* SwbAuthZSubject.SWB_DATASET_ACCESS_LEVEL */, `${dataset.id}`, [projectAdmin], ['READ', 'UPDATE', 'DELETE']);
        await this._addAuthZPermissionsForDataset(request.authenticatedUser, "SWB_DATASET_UPLOAD" /* SwbAuthZSubject.SWB_DATASET_UPLOAD */, dataset.id, [projectAdmin, projectResearcher], ['READ'], { projectId: { $eq: projectId } });
        return dataset;
    }
    async getSinglePartFileUploadUrl(dataSetId, fileName, authenticatedUser) {
        return this._workbenchDataSetService.getPresignedSinglePartUploadUrl(dataSetId, fileName, timeToLiveSeconds, this.storagePlugin, authenticatedUser);
    }
    async addAccessPermission(params) {
        const response = await this._dataSetsAuthService.addAccessPermission(params);
        return swb_app_1.PermissionsResponseParser.parse(response);
    }
    async getAccessPermissions(params) {
        const response = await this._dataSetsAuthService.getAccessPermissions(params);
        return swb_app_1.PermissionsResponseParser.parse(response);
    }
    async getAllDataSetAccessPermissions(datasetId) {
        const response = await this._dataSetsAuthService.getAllDataSetAccessPermissions(datasetId);
        return swb_app_1.PermissionsResponseParser.parse(response);
    }
    async removeAccessPermissions(params) {
        const response = await this._dataSetsAuthService.removeAccessPermissions(params);
        return swb_app_1.PermissionsResponseParser.parse(response);
    }
    async removeAllAccessPermissions(datasetId, authenticatedUser) {
        const response = await this._dataSetsAuthService.removeAllAccessPermissions(datasetId, authenticatedUser);
        return swb_app_1.PermissionsResponseParser.parse(response);
    }
    async addAccessForProject(request) {
        const projectAdmin = (0, roleUtils_1.getProjectAdminRole)(request.projectId);
        const projectResearcher = (0, roleUtils_1.getResearcherRole)(request.projectId);
        const requestedDataset = await this.getDataSet(request.dataSetId, request.authenticatedUser);
        if (requestedDataset.owner === projectAdmin) {
            throw new swb_app_1.ConflictError(`${request.projectId} already owns this dataset`);
        }
        const dataset = {
            type: workbench_core_base_1.resourceTypeToKey.dataset,
            id: request.dataSetId,
            data: {
                id: request.projectId,
                permission: request.accessLevel
            }
        };
        const project = {
            type: workbench_core_base_1.resourceTypeToKey.project,
            id: request.projectId,
            data: {
                id: request.dataSetId,
                permission: request.accessLevel
            }
        };
        const existingAssociation = await this._databaseService.getAssociation(dataset, project);
        if (existingAssociation) {
            throw new swb_app_1.ConflictError(`Project ${project.id} is already associated with Dataset ${dataset.id}`);
        }
        await this._addAuthZPermissionsForDataset(request.authenticatedUser, "SWB_DATASET" /* SwbAuthZSubject.SWB_DATASET */, request.dataSetId, [projectAdmin, projectResearcher], ['READ'], { projectId: { $eq: request.projectId } });
        const permissionRequest = {
            authenticatedUser: request.authenticatedUser,
            dataSetId: request.dataSetId,
            permission: [
                {
                    identity: projectAdmin,
                    identityType: 'GROUP',
                    accessLevel: request.accessLevel
                },
                {
                    identity: projectResearcher,
                    identityType: 'GROUP',
                    accessLevel: request.accessLevel
                }
            ]
        };
        const response = await this.addAccessPermission(permissionRequest);
        if (request.accessLevel === 'read-write') {
            await this._addAuthZPermissionsForDataset(request.authenticatedUser, "SWB_DATASET_UPLOAD" /* SwbAuthZSubject.SWB_DATASET_UPLOAD */, request.dataSetId, [projectAdmin, projectResearcher], ['READ'], { projectId: { $eq: request.projectId } });
        }
        await this._databaseService.storeAssociations(dataset, [project]);
        return response;
    }
    async removeAccessForProject(request) {
        const reqDataset = await this.getDataSet(request.dataSetId, request.authenticatedUser);
        const projectId = request.projectId;
        const projectAdmin = (0, roleUtils_1.getProjectAdminRole)(projectId);
        // Make sure you're not removing the access for your project
        if (projectAdmin === reqDataset.owner) {
            throw new swb_app_1.ConflictError(`${projectId} cannot remove access from ${request.dataSetId} for the ProjectAdmin because it owns that dataset.`);
        }
        const project = {
            type: workbench_core_base_1.resourceTypeToKey.project,
            id: projectId
        };
        // Make sure project has no environments with dataset mounted
        const projDatasetEndpointAssociations = await this._databaseService.listAssociations(project, `${workbench_core_base_1.resourceTypeToKey.dataset}#${request.dataSetId}${workbench_core_base_1.resourceTypeToKey.endpoint}`, { pageSize: 1 });
        if (projDatasetEndpointAssociations.data.length > 0) {
            throw new swb_app_1.ConflictError(`${projectId} still has environments with ${request.dataSetId} mounted. ` +
                'Terminate environments before removing access.');
        }
        const projectResearcher = (0, roleUtils_1.getResearcherRole)(projectId);
        await this._removeAuthZPermissionsForDataset(request.authenticatedUser, "SWB_DATASET" /* SwbAuthZSubject.SWB_DATASET */, request.dataSetId, [projectAdmin, projectResearcher], ['READ']);
        await this._removeAuthZPermissionsForDataset(request.authenticatedUser, "SWB_DATASET_UPLOAD" /* SwbAuthZSubject.SWB_DATASET_UPLOAD */, request.dataSetId, [projectAdmin, projectResearcher], ['READ']);
        // `read-write` will cause the read and write permissions to get removed,
        // so there is no need to pass in `read-only` when removing access.
        const accessLevel = 'read-write';
        const readWriteDeletionRequest = {
            authenticatedUser: request.authenticatedUser,
            dataSetId: request.dataSetId,
            permission: [
                {
                    identity: projectAdmin,
                    identityType: 'GROUP',
                    accessLevel
                },
                {
                    identity: projectResearcher,
                    identityType: 'GROUP',
                    accessLevel
                }
            ]
        };
        const response = await this.removeAccessPermissions(readWriteDeletionRequest);
        const dataset = {
            type: workbench_core_base_1.resourceTypeToKey.dataset,
            id: request.dataSetId
        };
        await this._databaseService.removeAssociations(dataset, [project]);
        return response;
    }
    async isProjectAuthorizedForDatasets(request) {
        let authorizedDataset = true;
        await Promise.all(request.datasetIds.map(async (datasetId) => {
            // Using Researcher role to verify project has access to dataset because Researcher and PA permissions
            // are coupled together in regard to datasets
            const accessPermissions = await this.getAccessPermissions({
                dataSetId: datasetId,
                identity: `${request.projectId}#Researcher`,
                identityType: 'GROUP'
            });
            if (accessPermissions.data.permissions.length === 0) {
                authorizedDataset = false;
            }
        }));
        return authorizedDataset;
    }
    async _addAuthZPermissionsForDataset(authenticatedUser, subject, subjectId, roles, actions, conditions) {
        let partialIdentityPermission = {
            action: undefined,
            effect: 'ALLOW',
            identityId: undefined,
            identityType: 'GROUP',
            subjectId: subjectId,
            subjectType: subject
        };
        if (conditions) {
            partialIdentityPermission = Object.assign(Object.assign({}, partialIdentityPermission), { conditions: conditions });
        }
        const identityPermissions = [];
        for (const role of roles) {
            for (const action of actions) {
                const identityPermission = workbench_core_authorization_1.IdentityPermissionParser.parse(Object.assign(Object.assign({}, partialIdentityPermission), { identityId: role, action }));
                identityPermissions.push(identityPermission);
            }
        }
        const createRequest = workbench_core_authorization_1.CreateIdentityPermissionsRequestParser.parse({
            authenticatedUser,
            identityPermissions
        });
        await this._dynamicAuthService.createIdentityPermissions(createRequest);
    }
    async _removeAuthZPermissionsForDataset(authenticatedUser, subject, subjectId, roles, actions) {
        const partialIdentityPermission = {
            action: undefined,
            effect: 'ALLOW',
            identityId: undefined,
            identityType: 'GROUP',
            subjectId: subjectId,
            subjectType: subject
        };
        const identityPermissions = [];
        for (const role of roles) {
            for (const action of actions) {
                const identityPermission = workbench_core_authorization_1.IdentityPermissionParser.parse(Object.assign(Object.assign({}, partialIdentityPermission), { identityId: role, action }));
                identityPermissions.push(identityPermission);
            }
        }
        const createRequest = workbench_core_authorization_1.CreateIdentityPermissionsRequestParser.parse({
            authenticatedUser,
            identityPermissions
        });
        await this._dynamicAuthService.deleteIdentityPermissions(createRequest);
    }
}
exports.DataSetService = DataSetService;
//# sourceMappingURL=dataSetService.js.map