"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const decorators_1 = require("../decorators");
const shared_1 = require("../shared");
const Server_1 = require("../Server");
const Token_1 = require("../shared/Token");
const realms_1 = require("../realms");
const errors = require("../errors");
const moment = require("moment");
const URI = require("urijs");
const _ = require("lodash");
const events_1 = require("events");
let PermissionsService = PermissionsService_1 = class PermissionsService extends events_1.EventEmitter {
    constructor() {
        super(...arguments);
        this.pendingHandlerCallbacks = 0;
    }
    static extractPermission(p) {
        return {
            userId: p.user && p.user.userId,
            realmOwnerId: p.realmFile && p.realmFile.owner && p.realmFile.owner.userId,
            realmPath: p.realmFile && p.realmFile.path,
            mayRead: p.mayRead,
            mayWrite: p.mayWrite,
            mayManage: p.mayManage,
            updatedAt: p.updatedAt
        };
    }
    static ensureInTransaction(mirror) {
        if (!mirror.isInTransaction) {
            throw new Error("user permission reflection attepmted outside of transaction");
        }
    }
    static getPrimaryKey(permission) {
        return `${permission.userId || '*'}-${permission.realmPath}`;
    }
    getManagementRealmForUserId(userId) {
        return __awaiter(this, void 0, void 0, function* () {
            const realmDefinition = realms_1.CreateUserManagementRealmDefinition(userId);
            return this.server.openRealm(realmDefinition);
        });
    }
    getUserSpecificPermissionRealm(userId, legacy = false) {
        const realmDefinition = legacy ? realms_1.CreateLegacyUserPermissionRealmDefinition(userId) : realms_1.CreateUserPermissionRealmDefinition(userId);
        return this.server.openRealm(realmDefinition);
    }
    start(server) {
        return __awaiter(this, void 0, void 0, function* () {
            this.server = server;
            this.publicKey = server.publicKey;
            this.logger = server.logger;
            this.adminRealm = yield server.openRealm(realms_1.AdminRealm);
            this.legacyWildcardPermissionRealm = yield server.openRealm(realms_1.LegacyWildcardPermissionRealm);
            this.wildcardPermissionRealm = yield server.openRealm(realms_1.WildcardPermissionRealm);
            const syncBaseUrl = `realm://${server.address}`;
            const authUrl = `http://${server.address}`;
            const adminUser = Realm.Sync.User.adminUser(server.adminToken, authUrl);
            this.handleAdminChangeCallback = this.handleAdminRealmChange.bind(this);
            Realm.Sync.addListener(syncBaseUrl, adminUser, PermissionsService_1.ADMIN_REALM_REGEX, 'change', this.handleAdminChangeCallback);
            this.handleManagementChangeCallback = this.handleManagementRealmChange.bind(this);
            Realm.Sync.addListener(syncBaseUrl, adminUser, PermissionsService_1.MANAGEMENT_REALM_REGEX, 'change', this.handleManagementChangeCallback);
            yield server.discovery.waitForService("realms");
            this.grantReadOnlyAccessToWildcardPermissionRealm();
            this.grantReadWriteOnLegacyUserPermissionRealms();
        });
    }
    stopping() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.handleManagementChangeCallback) {
                yield Realm.Sync.removeListener(PermissionsService_1.MANAGEMENT_REALM_REGEX, 'change', this.handleManagementChangeCallback);
                delete this.handleManagementChangeCallback;
            }
            if (this.handleAdminChangeCallback) {
                yield Realm.Sync.removeListener(PermissionsService_1.ADMIN_REALM_REGEX, 'change', this.handleAdminChangeCallback);
                delete this.handleAdminChangeCallback;
            }
            while (this.pendingHandlerCallbacks > 0) {
                yield shared_1.delay(100);
            }
        });
    }
    stop() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.adminRealm) {
                this.adminRealm.close();
                delete this.adminRealm;
            }
            if (this.wildcardPermissionRealm) {
                this.wildcardPermissionRealm.close();
                delete this.wildcardPermissionRealm;
            }
            if (this.legacyWildcardPermissionRealm) {
                this.legacyWildcardPermissionRealm.close();
                delete this.legacyWildcardPermissionRealm;
            }
        });
    }
    onDemand(req) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!req.authToken) {
                throw new errors.JSONError({
                    title: "Authorization header is not valid",
                    status: 400,
                });
            }
            const userId = req.authToken.identity;
            const permissionsInAdminRealm = this.adminRealm.objects('Permission')
                .filtered('user.userId = $0 || realmFile.owner.userId = $0', userId);
            for (const legacy of [true, false]) {
                const userSpecificRealm = yield this.getUserSpecificPermissionRealm(userId, legacy);
                try {
                    let didUpdateAnyPermission = false;
                    userSpecificRealm.beginTransaction();
                    for (const permissionFromAdminRealm of permissionsInAdminRealm.filtered("user != null").map(PermissionsService_1.extractPermission)) {
                        if (this.reflectPermissionIn(permissionFromAdminRealm, userSpecificRealm, legacy)) {
                            didUpdateAnyPermission = true;
                        }
                    }
                    if (didUpdateAnyPermission) {
                        userSpecificRealm.commitTransaction();
                    }
                    else {
                        userSpecificRealm.cancelTransaction();
                    }
                }
                finally {
                    userSpecificRealm.close();
                }
            }
            return {
                userId: userId,
                affectedPermissionsCount: permissionsInAdminRealm.length
            };
        });
    }
    applyPermissions(req, permissionRequest) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!Token_1.isAdminToken(req.authToken)) {
                throw new errors.realm.AccessDenied();
            }
            const mayManage = permissionRequest.accessLevel === 'admin';
            const mayWrite = mayManage || permissionRequest.accessLevel === 'write';
            const mayRead = mayWrite || permissionRequest.accessLevel === 'read';
            let userIds;
            let affectedUsers;
            const condition = permissionRequest.condition;
            if (condition.hasOwnProperty('userId')) {
                if (condition.userId === '*') {
                    affectedUsers = -1;
                    userIds = [null];
                }
                else {
                    userIds = [condition.userId];
                }
            }
            else if (condition.hasOwnProperty('metadataKey')) {
                userIds = this.getUserIdsWithMetadata(condition.metadataKey, condition.metadataValue);
            }
            else {
                throw new errors.realm.InvalidParameters('condition');
            }
            if (!affectedUsers) {
                affectedUsers = userIds.length;
            }
            for (const userId of userIds) {
                this.applyPermissionChangeInAdminRealm({
                    userId,
                    realmPath: permissionRequest.realmPath,
                    mayRead,
                    mayWrite,
                    mayManage
                });
            }
            return {
                affectedUsers
            };
        });
    }
    unreflectPermissionIn(permission, mirror, legacy = false) {
        PermissionsService_1.ensureInTransaction(mirror);
        let reflection;
        if (legacy) {
            if (permission.userId) {
                reflection = mirror.objects('Permission')
                    .filtered('userId == $0 && path == $1', permission.userId, permission.realmPath)[0];
            }
            else {
                reflection = mirror.objects('Permission')
                    .filtered('path == $0', permission.realmPath)[0];
            }
        }
        else {
            const pk = PermissionsService_1.getPrimaryKey(permission);
            reflection = mirror.objectForPrimaryKey("Permission", pk);
        }
        if (!reflection) {
            return false;
        }
        mirror.delete(reflection);
        return true;
    }
    reflectPermissionIn(permission, mirror, legacy = false) {
        PermissionsService_1.ensureInTransaction(mirror);
        let reflection;
        const update = {};
        if (legacy) {
            if (permission.userId) {
                reflection = mirror.objects('Permission')
                    .filtered('userId == $0 && path == $1', permission.userId, permission.realmPath)[0];
            }
            else {
                reflection = mirror.objects('Permission')
                    .filtered('path == $0', permission.realmPath)[0];
            }
        }
        else {
            const pk = PermissionsService_1.getPrimaryKey(permission);
            reflection = mirror.objectForPrimaryKey("Permission", pk);
            update.userPath = pk;
        }
        const accessControlProperties = ['mayRead', 'mayWrite', 'mayManage'];
        if (reflection) {
            for (const key of accessControlProperties) {
                if (reflection[key] !== permission[key]) {
                    update[key] = permission[key];
                }
            }
        }
        else {
            Object.assign(update, {
                userId: permission.userId || '*',
                path: permission.realmPath,
                mayRead: permission.mayRead,
                mayWrite: permission.mayWrite,
                mayManage: permission.mayManage,
                updatedAt: permission.updatedAt,
            });
        }
        const updatedKeys = _.intersection(Object.keys(update), accessControlProperties);
        if (updatedKeys.length === 0) {
            return false;
        }
        if (legacy) {
            if (reflection) {
                Object.assign(reflection, update);
            }
            else {
                mirror.create("Permission", update);
            }
        }
        else {
            mirror.create("Permission", update, true);
        }
        return true;
    }
    getUserByUserId(userId) {
        return this.adminRealm.objectForPrimaryKey('User', userId);
    }
    getUserIdsWithMetadata(key, value) {
        const userIds = new Set();
        if (key === 'email') {
            const account = this.adminRealm.objects('Account')
                .filtered(`provider = $0 AND providerId = $1`, 'password', value)[0];
            if (account && account.users.length > 0) {
                userIds.add(account.users[0].userId);
            }
        }
        if (key && value) {
            this.adminRealm.objects('UserMetadataRow')
                .filtered("key = $0 AND value = $1", key, value)
                .filter(m => m.users.length > 0)
                .forEach(m => userIds.add(m.users[0].userId));
        }
        return Array.from(userIds);
    }
    grantReadOnlyAccessToWildcardPermissionRealm() {
        const exposeAsReadOnly = (realmPath) => {
            const wildcardRealmFile = this.adminRealm.objectForPrimaryKey('RealmFile', realmPath);
            if (!wildcardRealmFile) {
                this.logger.error('Could not find the WildcardPermission RealmFile in Admin Realm!');
            }
            else {
                let wildcardPerm = wildcardRealmFile.permissions.filtered('user == null')[0];
                if (!wildcardPerm) {
                    this.adminRealm.write(() => {
                        wildcardPerm = this.adminRealm.create('Permission', {
                            user: null,
                            realmFile: wildcardRealmFile,
                            updatedAt: new Date(),
                            mayRead: true,
                            mayWrite: false,
                            mayManage: false,
                        });
                    });
                }
            }
        };
        exposeAsReadOnly(realms_1.LegacyWildcardPermissionRealm.remotePath);
        exposeAsReadOnly(realms_1.WildcardPermissionRealm.remotePath);
    }
    grantReadWriteOnLegacyUserPermissionRealms() {
        this.adminRealm.write(() => {
            const wrongPerms = this.adminRealm.objects('Permission')
                .filtered('user != null AND mayWrite != true AND realmFile.path ENDSWITH "/__permission"');
            for (const p of wrongPerms) {
                if (p.realmFile.path === `/${p.user.userId}/__permission`) {
                    this.logger.debug(`granting read-write on ${p.realmFile.path} to ${p.user.userId}`);
                    p.mayRead = true;
                    p.mayWrite = true;
                }
            }
        });
    }
    applyPermissionChangeInAdminRealm(input) {
        const realmFile = this.adminRealm.objectForPrimaryKey("RealmFile", input.realmPath);
        if (!realmFile) {
            throw new errors.realm.InvalidParameters({ name: 'realmFilePath', reason: `The provided realmFilePath (${input.realmPath}) does not exist.` });
        }
        let permission;
        let user;
        const shouldDelete = (input.mayRead === false && input.mayWrite === false && input.mayManage === false);
        this.adminRealm.write(() => {
            if (input.userId) {
                user = this.getUserByUserId(input.userId);
                permission = realmFile.permissions.filtered('user.userId = $0', input.userId)[0];
            }
            else {
                permission = realmFile.permissions.filtered('user == null')[0];
            }
            if (permission) {
                if (shouldDelete) {
                    this.adminRealm.delete(permission);
                }
                else {
                    if (input.mayRead != null) {
                        permission.mayRead = input.mayRead;
                    }
                    if (input.mayWrite != null) {
                        permission.mayWrite = input.mayWrite;
                    }
                    if (input.mayManage != null) {
                        permission.mayManage = input.mayManage;
                    }
                    permission.updatedAt = new Date();
                }
            }
            else if (!shouldDelete) {
                permission = this.adminRealm.create('Permission', {
                    user: user,
                    realmFile: realmFile,
                    updatedAt: new Date(),
                    mayRead: input.mayRead || false,
                    mayWrite: input.mayWrite || false,
                    mayManage: input.mayManage || false
                });
            }
        });
    }
    handleAdminRealmChange(changeEvent) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.handleGNCallback(() => __awaiter(this, void 0, void 0, function* () {
                const permissionChanges = changeEvent.changes['Permission'];
                if (permissionChanges) {
                    const getRealmsForReflection = (permission) => __awaiter(this, void 0, void 0, function* () {
                        const mirrors = [];
                        const legacyMirrors = [];
                        if (permission.userId) {
                            const granteeId = permission.userId;
                            const grantorId = permission.realmOwnerId;
                            mirrors.push(yield this.getUserSpecificPermissionRealm(granteeId));
                            legacyMirrors.push(yield this.getUserSpecificPermissionRealm(granteeId, true));
                            if (grantorId && granteeId !== grantorId) {
                                mirrors.push(yield this.getUserSpecificPermissionRealm(grantorId));
                                legacyMirrors.push(yield this.getUserSpecificPermissionRealm(grantorId, true));
                            }
                        }
                        else {
                            this.wildcardPermissionRealm.open();
                            this.legacyWildcardPermissionRealm.open();
                            mirrors.push(this.wildcardPermissionRealm);
                            legacyMirrors.push(this.legacyWildcardPermissionRealm);
                        }
                        return {
                            mirrors,
                            legacyMirrors
                        };
                    });
                    const writeInAppropriateMirrors = (permission, shouldDelete) => __awaiter(this, void 0, void 0, function* () {
                        const { mirrors, legacyMirrors } = yield getRealmsForReflection(permission);
                        try {
                            const operation = shouldDelete ? this.unreflectPermissionIn : this.reflectPermissionIn;
                            for (const mirror of mirrors) {
                                mirror.write(() => {
                                    operation(permission, mirror, false);
                                });
                            }
                            for (const mirror of legacyMirrors) {
                                mirror.write(() => {
                                    operation(permission, mirror, true);
                                });
                            }
                        }
                        finally {
                            for (const mirror of [...mirrors, ...legacyMirrors]) {
                                mirror.close();
                            }
                        }
                    });
                    this.emit('handlePermissionReflection', changeEvent);
                    const promises = new Array();
                    if (permissionChanges.insertions.length > 0 ||
                        permissionChanges.modifications.length > 0) {
                        const collection = changeEvent.realm.objects('Permission');
                        const insertedPermissions = permissionChanges.insertions.map(i => PermissionsService_1.extractPermission(collection[i]));
                        const modifiedPermissions = permissionChanges.modifications.map(i => PermissionsService_1.extractPermission(collection[i]));
                        promises.push(...insertedPermissions.concat(modifiedPermissions).map(p => writeInAppropriateMirrors(p, false)));
                    }
                    if (permissionChanges.deletions.length > 0) {
                        const oldCollection = changeEvent.oldRealm.objects('Permission');
                        const deletedPermissions = permissionChanges.deletions.map(i => PermissionsService_1.extractPermission(oldCollection[i]));
                        promises.push(...deletedPermissions.map(p => writeInAppropriateMirrors(p, true)));
                    }
                    yield Promise.all(promises);
                }
            }));
        });
    }
    handleManagementRealmChange(changeEvent) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.handleGNCallback(() => __awaiter(this, void 0, void 0, function* () {
                const matches = changeEvent.path.match(PermissionsService_1.MANAGEMENT_REALM_REGEX);
                if (!matches) {
                    return;
                }
                let userId = matches[1];
                if (userId === '__auth') {
                    userId = '__admin';
                }
                const owner = this.getUserByUserId(userId);
                if (!owner) {
                    this.logger.error(`Received change notification for management Realm without an owner: ${changeEvent.path}`);
                    return;
                }
                const realm = changeEvent.realm;
                this.logger.debug(`A management realm with path ${changeEvent.path} has changes with userId: ${userId}`);
                if (realm.schema.findIndex(objSchema => objSchema.name === 'PermissionOffer') !== -1) {
                    const unprocessedPermissionOfferObjects = realm.objects("PermissionOffer")
                        .filtered('statusCode == null')
                        .snapshot();
                    for (const p of unprocessedPermissionOfferObjects) {
                        this.handlePermissionOfferObject(p, owner, realm);
                    }
                }
                if (realm.schema.findIndex(objSchema => objSchema.name === 'PermissionOfferResponse') !== -1) {
                    const unprocessedPermissionOfferResponseObjects = realm.objects("PermissionOfferResponse")
                        .filtered('statusCode == null')
                        .snapshot()
                        .map(p => this.handlePermissionOfferResponseObject(p, owner, realm));
                    yield Promise.all(unprocessedPermissionOfferResponseObjects);
                }
                if (realm.schema.findIndex(objSchema => objSchema.name === 'PermissionChange') !== -1) {
                    const unprocessedPermissionChangeObjects = realm.objects("PermissionChange")
                        .filtered('statusCode == null')
                        .snapshot();
                    for (const p of unprocessedPermissionChangeObjects) {
                        this.handlePermissionChangeObject(p, owner, realm);
                    }
                }
            }));
        });
    }
    handleGNCallback(handler) {
        return __awaiter(this, void 0, void 0, function* () {
            this.pendingHandlerCallbacks++;
            try {
                yield handler();
            }
            catch (err) {
                this.logger.error('An error occurred while executing GN callback', err);
            }
            finally {
                this.pendingHandlerCallbacks--;
            }
        });
    }
    handlePermissionOfferObject(permissionOffer, owner, realm) {
        try {
            const realmPath = new URI(permissionOffer.realmUrl).path().replace('~', owner.userId);
            this.validateUserIdCanChangePermissionsForPath(owner, realmPath);
            if (permissionOffer.expiresAt && moment(permissionOffer.expiresAt).isBefore(moment.now())) {
                throw new errors.realm.ExpiredPermissionOffer();
            }
            realm.write(() => {
                permissionOffer.realmUrl = realmPath;
                permissionOffer.token = `${owner.userId}:${permissionOffer.id}`;
                permissionOffer.statusCode = 0;
            });
        }
        catch (err) {
            const problem = err;
            this.logger.error(problem.toString());
            realm.write(() => {
                permissionOffer.statusMessage = problem.title;
                permissionOffer.statusCode = problem.code;
            });
        }
    }
    handlePermissionOfferResponseObject(permissionOfferResponse, owner, realm) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const tokenComponents = permissionOfferResponse.token.split(':');
                if (tokenComponents.length < 2 || tokenComponents[0].length === 0) {
                    throw new errors.realm.InvalidParameters('token');
                }
                const offeringUserId = tokenComponents[0];
                const offeringRealm = yield this.getManagementRealmForUserId(offeringUserId);
                try {
                    const permissionOffers = offeringRealm.objects("PermissionOffer")
                        .filtered('token = $0', permissionOfferResponse.token);
                    if (permissionOffers.length === 0) {
                        throw new errors.realm.InvalidParameters('token');
                    }
                    if (permissionOffers.length > 1) {
                        throw new errors.realm.AmbiguousPermissionOfferToken({
                            detail: `Unexpected count of permission offers (=${permissionOffers.length})`
                                + ` matching the token '${permissionOfferResponse.token}' of the permission request!`,
                        });
                    }
                    const permissionOffer = permissionOffers[0];
                    if (permissionOffer.expiresAt && moment(permissionOffer.expiresAt).isBefore(moment.now())) {
                        throw new errors.realm.ExpiredPermissionOffer();
                    }
                    const realmPath = new URI(permissionOffer.realmUrl).path();
                    const mayRead = permissionOffer.mayRead || null;
                    const mayWrite = permissionOffer.mayWrite || null;
                    const mayManage = permissionOffer.mayManage || null;
                    this.applyPermissionChangeInAdminRealm({
                        realmPath: realmPath,
                        userId: owner.userId,
                        mayRead,
                        mayWrite,
                        mayManage
                    });
                    realm.write(() => {
                        permissionOfferResponse.realmUrl = realmPath;
                        permissionOfferResponse.statusCode = 0;
                    });
                }
                finally {
                    offeringRealm.close();
                }
            }
            catch (err) {
                const problem = err;
                this.logger.error(problem.toString());
                realm.write(() => {
                    permissionOfferResponse.statusMessage = problem.title;
                    permissionOfferResponse.statusCode = problem.code;
                });
            }
        });
    }
    handlePermissionChangeObject(permissionChange, owner, realm) {
        try {
            const realmPath = new URI(permissionChange.realmUrl).path().replace('~', owner.userId);
            if (permissionChange.realmUrl === '*') {
                const userId = permissionChange.userId === '*' ? null : permissionChange.userId;
                this.adminRealm.objects(realms_1.RealmFile.schema.name)
                    .filtered('owner = $0', owner)
                    .forEach(file => this.applyPermissionChangeInAdminRealm({
                    realmPath: file.path,
                    userId,
                    mayRead: permissionChange.mayRead,
                    mayWrite: permissionChange.mayWrite,
                    mayManage: permissionChange.mayManage
                }));
            }
            else {
                this.validateUserIdCanChangePermissionsForPath(owner, realmPath);
                if (permissionChange.userId === '*') {
                    this.applyPermissionChangeInAdminRealm({
                        realmPath: realmPath,
                        userId: null,
                        mayRead: permissionChange.mayRead,
                        mayWrite: permissionChange.mayWrite,
                        mayManage: permissionChange.mayManage
                    });
                }
                else {
                    let userIds;
                    if (permissionChange.metadataKey) {
                        userIds = this.getUserIdsWithMetadata(permissionChange.metadataKey, permissionChange.metadataValue);
                        if (userIds.length === 0) {
                            throw new errors.realm.InvalidParameters({ name: 'metadataValue', reason: `The provided metadata value (${permissionChange.metadataValue}) matched no users.` });
                        }
                        this.logger.debug(`Apply permission change by metadata query
                            (${permissionChange.metadataKey}=${permissionChange.metadataValue})
                            matching the userIds ${userIds}`);
                    }
                    else {
                        this.logger.debug(`Apply permission change for user ${permissionChange.userId}`);
                        userIds = [permissionChange.userId];
                    }
                    const realmFiles = this.adminRealm.objects(realms_1.RealmFile.schema.name).filtered('path = $0', realmPath);
                    for (const file of realmFiles) {
                        for (const userId of userIds) {
                            this.applyPermissionChangeInAdminRealm({
                                realmPath: file.path,
                                userId,
                                mayRead: permissionChange.mayRead,
                                mayWrite: permissionChange.mayWrite,
                                mayManage: permissionChange.mayManage
                            });
                        }
                    }
                }
            }
            realm.write(() => {
                permissionChange.statusCode = 0;
                permissionChange.statusMessage = `Successfully applied PermissionChange for ${realmPath}`;
            });
        }
        catch (err) {
            const problem = err;
            this.logger.error(problem.toString());
            realm.write(() => {
                permissionChange.statusMessage = problem.title;
                permissionChange.statusCode = problem.code;
            });
        }
    }
    validateUserIdCanChangePermissionsForPath(owner, path) {
        if (_.includes(path, '__management')) {
            throw new errors.realm.InvalidParameters({
                name: 'realmUrl',
                reason: 'The realmUrl cannot be of a management realm'
            });
        }
        if (_.includes(path, '__permission')) {
            throw new errors.realm.InvalidParameters({
                name: 'realmUrl',
                reason: 'The realmUrl cannot be of a permission realm'
            });
        }
        if (_.includes(path, '__perm')) {
            throw new errors.realm.InvalidParameters({
                name: 'realmUrl',
                reason: 'The realmUrl cannot be of a permission realm'
            });
        }
        const realmFile = this.adminRealm.objectForPrimaryKey(realms_1.RealmFile.schema.name, path);
        if (!realmFile) {
            throw new errors.realm.InvalidParameters({
                name: 'realmUrl',
                reason: 'The realmUrl does not exist'
            });
        }
        const permissions = realmFile.permissions.filtered('(user = $0 OR user == null) AND mayManage = true', owner);
        if (!owner.isAdmin && permissions.length === 0) {
            throw new errors.realm.AccessDenied();
        }
    }
};
PermissionsService.MANAGEMENT_REALM_REGEX = '^/([^/]+)/__management$';
PermissionsService.ADMIN_REALM_REGEX = `/__admin`;
__decorate([
    decorators_1.Start(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Server_1.Server]),
    __metadata("design:returntype", Promise)
], PermissionsService.prototype, "start", null);
__decorate([
    decorators_1.Stopping(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], PermissionsService.prototype, "stopping", null);
__decorate([
    decorators_1.Stop(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], PermissionsService.prototype, "stop", null);
__decorate([
    decorators_1.Get('/ondemand'),
    __param(0, decorators_1.Request()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], PermissionsService.prototype, "onDemand", null);
__decorate([
    decorators_1.Post('/apply'),
    __param(0, decorators_1.Request()), __param(1, decorators_1.Body()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object]),
    __metadata("design:returntype", Promise)
], PermissionsService.prototype, "applyPermissions", null);
PermissionsService = PermissionsService_1 = __decorate([
    decorators_1.BaseRoute('/permissions'),
    decorators_1.ServiceName('permissions')
], PermissionsService);
exports.PermissionsService = PermissionsService;
var PermissionsService_1;
//# sourceMappingURL=PermissionsService.js.map