"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 fs = require("fs-extra");
const path = require("path");
const URI = require("urijs");
const moment = require("moment");
const crypto = require("crypto");
const useragent = require("express-useragent");
const Queue = require("js-queue");
const util_1 = require("../shared/util");
const decorators_1 = require("../decorators");
const Server_1 = require("../Server");
const Token_1 = require("../shared/Token");
const errors = require("../errors");
const realms_1 = require("../realms");
const Token_2 = require("../shared/Token");
const SyncClient_1 = require("../service-clients/SyncClient");
let AuthService = AuthService_1 = class AuthService {
    constructor(config = {}) {
        this.providers = [];
        this.providerQueue = new Queue();
        this.runtimeConfigurationEnabled = config.enableRuntimeConfiguration;
        this.refreshTokenTtl = config.refreshTokenTtl || moment.duration(10, 'years').asSeconds();
        this.userTokenTtl = config.userTokenTtl || moment.duration(1, 'hours').asSeconds();
        this.accessTokenTtl = config.accessTokenTtl || moment.duration(6, 'minutes').asSeconds();
    }
    addProvider(provider) {
        if (this.runtimeConfigurationEnabled === true) {
            throw new Error('The manual provider API is incompatible with the runtime configuration API (setDefaultProviders).');
        }
        this.runtimeConfigurationEnabled = false;
        this.providers.push(provider);
    }
    setDefaultProviders(providers) {
        if (this.runtimeConfigurationEnabled === false) {
            throw new Error('The runtime configuration API is incompatible with the manual provider API (addProvider).');
        }
        this.runtimeConfigurationEnabled = true;
        this.defaultProviders = providers;
    }
    getUserByProviderId(provider, providerId) {
        const accounts = this.adminRealm.objects('Account').filtered(`provider = $0 AND providerId = $1`, provider, providerId);
        if (accounts.length > 0) {
            return accounts[0].users[0];
        }
        return undefined;
    }
    createOrUpdateUser(providerId, provider, isAdmin, metadata = {}, userId) {
        return __awaiter(this, void 0, void 0, function* () {
            const missingParameters = new Array();
            if (!provider) {
                missingParameters.push('provider');
            }
            if (!providerId) {
                missingParameters.push('providerId');
            }
            if (missingParameters.length > 0) {
                throw new errors.realm.MissingParameters(...missingParameters);
            }
            if (userId) {
                const encodedUserId = encodeURIComponent(userId);
                if (userId !== encodedUserId) {
                    throw new errors.realm.InvalidParameters({
                        name: 'userId',
                        reason: 'Realm requires the userId that does not require URI encoding.'
                    });
                }
            }
            let user = this.getUserByProviderId(provider, providerId);
            if (user && userId && userId !== user.userId) {
                throw new errors.realm.InvalidParameters({
                    name: 'userId',
                    reason: "userId does not match the user found by provider/providerId"
                });
            }
            this.adminRealm.write(() => {
                if (!user) {
                    user = this.adminRealm.create('User', {
                        userId: userId || this.generateUniqueId(),
                        isAdmin: isAdmin || false,
                    });
                    user.accounts.push({
                        provider: provider,
                        providerId: providerId
                    });
                    user.created = true;
                }
                else {
                    user.created = false;
                    if (isAdmin !== undefined) {
                        user.isAdmin = isAdmin;
                    }
                }
                this.updateUserMetadata(user, metadata);
            });
            return user;
        });
    }
    updateUserMetadata(user, metadata = {}) {
        const realmUser = (typeof user === 'string') ? this.adminRealm.objectForPrimaryKey('User', user) : user;
        const update = () => {
            this.adminRealm.delete(realmUser.metadata);
            for (const key in metadata) {
                realmUser.metadata.push({
                    key: key,
                    value: metadata[key]
                });
            }
        };
        if (this.adminRealm.isInTransaction) {
            update();
        }
        else {
            this.adminRealm.write(update);
        }
    }
    start(server) {
        return __awaiter(this, void 0, void 0, function* () {
            this.logger = server.logger.withContext({ service: "auth" });
            this.server = server;
            this.privateKey = server.privateKey;
            this.publicKey = server.publicKey;
            this.adminToken = server.adminToken;
            this.adminRealm = yield server.openRealm(realms_1.AdminRealm);
            this.tokenRevocationRealm = yield server.openRealm(realms_1.TokenRevocationRealm);
            this.configurationRealm = yield server.openRealm(realms_1.ConfigurationRealm);
            try {
                const userId = "__admin";
                let foundUser = this.adminRealm.objectForPrimaryKey('User', userId);
                if (!foundUser) {
                    foundUser = yield this.createOrUpdateUser(userId, 'realm', true, {}, userId);
                    this.logger.info(`Autocreated admin token user: ${userId}`);
                }
            }
            catch (err) {
                this.logger.error('Failed to autocreate admin token user', err);
            }
            this.pruneRevocationTokens();
            this.cleanupInterval = setInterval(this.pruneRevocationTokens.bind(this), 1000 * 60 * 60 * 24);
            if (this.runtimeConfigurationEnabled === undefined) {
                this.runtimeConfigurationEnabled = true;
            }
            if (this.runtimeConfigurationEnabled) {
                const providerConfigs = this.configurationRealm.objects('AuthProviderConfig');
                if (providerConfigs.length === 0 || this.defaultProviders.findIndex(p => p.forceCodeConfig) > -1) {
                    const providers = this.defaultProviders.filter(p => providerConfigs.length === 0 || p.forceCodeConfig);
                    let shouldCommitTransaction = false;
                    this.configurationRealm.beginTransaction();
                    for (const provider of providers) {
                        const name = provider.name || provider.type;
                        const config = provider.config ? JSON.stringify(provider.config) : null;
                        const forceCodeConfig = provider.forceCodeConfig === true;
                        const existing = this.configurationRealm.objectForPrimaryKey('AuthProviderConfig', name);
                        const hasChanges = !existing ||
                            existing.name !== name ||
                            existing.type !== provider.type ||
                            existing.forceCodeConfig !== forceCodeConfig ||
                            existing.config !== config;
                        if (hasChanges) {
                            this.configurationRealm.create('AuthProviderConfig', {
                                name: name,
                                type: provider.type,
                                config: config,
                                forceCodeConfig: forceCodeConfig
                            }, true);
                            shouldCommitTransaction = true;
                        }
                    }
                    if (shouldCommitTransaction) {
                        this.configurationRealm.commitTransaction();
                    }
                    else {
                        this.configurationRealm.cancelTransaction();
                    }
                }
                for (const providerConfig of providerConfigs) {
                    const provider = this.createProvider(providerConfig);
                    this.providers.push(provider);
                }
                providerConfigs.addListener((collection, changes) => {
                    const containsChanges = changes.deletions.length ||
                        changes.modifications.length ||
                        changes.insertions.length;
                    if (containsChanges) {
                        this.enqueueProviderOperation(() => __awaiter(this, void 0, void 0, function* () {
                            yield Promise.all(this.providers.map(p => this.stopProvider(p)));
                            this.providers.length = 0;
                            yield Promise.all(providerConfigs.map((config) => __awaiter(this, void 0, void 0, function* () {
                                try {
                                    const provider = this.createProvider(config);
                                    this.providers.push(provider);
                                    yield this.startProvider(provider);
                                }
                                catch (err) {
                                    this.logger.error(`Failed to start provider ${config.type}(${config.name}) with config ${config.config}. Error: ${err}`);
                                }
                            })));
                        }));
                    }
                });
            }
            yield new Promise((resolve) => {
                this.enqueueProviderOperation(() => __awaiter(this, void 0, void 0, function* () {
                    const promises = this.providers.map((p) => __awaiter(this, void 0, void 0, function* () {
                        try {
                            this.logger.detail(`Starting auth provider '${p.name}'`);
                            yield this.startProvider(p);
                        }
                        catch (err) {
                            this.logger.error(`Failed to start provider ${p.name} during initial boot. Error: ${err}`);
                        }
                    }));
                    yield Promise.all(promises);
                    resolve();
                }));
            });
        });
    }
    stopping() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.stopProviderQueue();
            yield Promise.all(this.providers.map(provider => {
                return this.stopProvider(provider);
            }));
            delete (this.providers);
        });
    }
    stop() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.configurationRealm) {
                this.configurationRealm.close();
                delete this.configurationRealm;
            }
            if (this.cleanupInterval) {
                clearInterval(this.cleanupInterval);
            }
            if (this.tokenRevocationRealm) {
                this.tokenRevocationRealm.close();
                delete this.tokenRevocationRealm;
            }
            if (this.adminRealm) {
                this.adminRealm.close();
                delete this.adminRealm;
            }
        });
    }
    authenticate(body) {
        return __awaiter(this, void 0, void 0, function* () {
            const providerName = body.provider;
            const appId = body["app_id"] || 'io.realm.Auth';
            if (!providerName) {
                throw new errors.realm.MissingParameters('provider');
            }
            if (providerName === 'realm') {
                const data = body.data;
                if (!data) {
                    throw new errors.realm.MissingParameters('data');
                }
                let refreshToken;
                try {
                    refreshToken = this.server.tokenValidator.parseRefreshToken(data);
                }
                catch (err) {
                    throw new errors.realm.AccessDenied();
                }
                if (body['path']) {
                    const path = util_1.validateRealmPath(body['path'], refreshToken.identity);
                    return yield this.accessToken(path, refreshToken, data, appId);
                }
                else {
                    return yield this.userToken(refreshToken, appId);
                }
            }
            const provider = this.providers.find((p) => p.name === providerName);
            if (!provider) {
                throw new errors.realm.InvalidParameters('provider');
            }
            const user = yield provider.authenticateOrCreateUser(body);
            const refreshToken = new Token_1.RefreshToken({
                appId,
                identity: user.userId,
                isAdmin: user.isAdmin,
                expires: moment().add(this.refreshTokenTtl, 'second').unix(),
                isEmailConfirmed: user.isEmailConfirmed()
            });
            return {
                refresh_token: {
                    token: refreshToken.sign(this.privateKey),
                    token_data: refreshToken.toJSON(),
                }
            };
        });
    }
    userToken(token, app_id) {
        return __awaiter(this, void 0, void 0, function* () {
            const expires = moment().add(this.userTokenTtl, 'seconds').unix();
            const userToken = new Token_1.UserToken({
                identity: token.identity,
                isAdmin: token.isAdmin,
                appId: app_id,
                expires: moment().add(this.userTokenTtl, 'seconds').unix(),
            });
            return {
                user_token: {
                    token: userToken.sign(this.privateKey),
                    token_data: userToken.toJSON(),
                }
            };
        });
    }
    accessToken(path, token, data, app_id) {
        return __awaiter(this, void 0, void 0, function* () {
            path = path.replace('~', token.identity);
            let partialSyncPath;
            const partialIndex = path.indexOf('/__partial/');
            if (partialIndex !== -1) {
                partialSyncPath = path;
                path = path.substring(0, partialIndex);
            }
            let mayRead = false;
            let mayWrite = false;
            let mayManage = false;
            let syncLabel = '';
            const shouldCreate = true;
            if (token.isAdmin) {
                mayRead = true;
                mayWrite = true;
                mayManage = true;
                if (partialSyncPath) {
                    const realmResponseReference = yield this.server.realmDirectoryClient.findByPath({
                        realmPath: path,
                        shouldCreate,
                        token: data,
                        realmType: realms_1.RealmType.reference
                    });
                    AuthService_1.ensureRealmOfType(realms_1.RealmType.reference, realmResponseReference.realmType);
                    const realmResponsePartial = yield this.server.realmDirectoryClient.findByPath({
                        realmPath: partialSyncPath,
                        shouldCreate,
                        token: data,
                        realmType: realms_1.RealmType.partial
                    });
                    syncLabel = realmResponsePartial.syncLabel;
                }
                else {
                    const realmResponse = yield this.server.realmDirectoryClient.findByPath({
                        realmPath: path,
                        shouldCreate,
                        token: data,
                        realmType: realms_1.RealmType.full
                    });
                    syncLabel = realmResponse.syncLabel;
                }
            }
            else {
                if (partialSyncPath) {
                    mayRead = true;
                    mayWrite = true;
                    const realmResponseReference = yield this.server.realmDirectoryClient.findByPath({
                        realmPath: path,
                        shouldCreate,
                        token: data,
                        realmType: realms_1.RealmType.reference
                    });
                    if (!realmResponseReference.exists) {
                        throw new errors.realm.AccessDenied({
                            detail: `Failed to create a reference realm for '${path};`
                        });
                    }
                    AuthService_1.ensureRealmOfType(realms_1.RealmType.reference, realmResponseReference.realmType);
                    const realmResponsePartial = yield this.server.realmDirectoryClient.findByPath({
                        realmPath: partialSyncPath,
                        shouldCreate,
                        token: data,
                        realmType: realms_1.RealmType.partial
                    });
                    syncLabel = realmResponsePartial.syncLabel;
                }
                else {
                    const realmResponse = yield this.server.realmDirectoryClient.findByPath({
                        realmPath: path,
                        shouldCreate,
                        token: data,
                        realmType: realms_1.RealmType.full
                    });
                    AuthService_1.ensureRealmOfType(realms_1.RealmType.full, realmResponse.realmType);
                    syncLabel = realmResponse.syncLabel;
                    yield util_1.waitForDownload(this.adminRealm);
                    const realmFile = this.adminRealm.objectForPrimaryKey('RealmFile', path);
                    if (!realmFile) {
                        throw new Error(`could not find realmfile: ${path}`);
                    }
                    const permissions = realmFile.permissions.filtered('user.userId = $0 OR user.userId = null', token.identity);
                    permissions.forEach(permission => {
                        mayRead = mayRead || permission.mayRead;
                        mayWrite = mayWrite || permission.mayWrite;
                        mayManage = mayManage || permission.mayManage;
                    });
                    if (!mayRead && !mayWrite && !mayManage) {
                        throw new errors.realm.AccessDenied();
                    }
                }
            }
            const access = [];
            if (mayRead) {
                access.push('download');
            }
            if (mayWrite) {
                access.push('upload');
            }
            if (mayManage) {
                access.push('manage');
            }
            const isAskingForTildePermission = ['__permission', '__perm'].some(name => path === `/${token.identity}/${name}`);
            if (isAskingForTildePermission) {
                yield this.server.permissionsClient.onDemand(data);
            }
            const appId = app_id || "io.realm.Auth";
            const expires = moment().add(this.accessTokenTtl, 'seconds').unix();
            const accessToken = new Token_1.AccessToken({
                identity: token.identity,
                appId,
                access: access,
                path: partialSyncPath || path,
                expires: moment().add(this.accessTokenTtl, 'seconds').unix(),
                syncLabel: syncLabel,
            });
            return {
                access_token: {
                    token: accessToken.sign(this.privateKey),
                    token_data: accessToken.toJSON(),
                }
            };
        });
    }
    revokeToken(tokenData, req) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!tokenData || tokenData === "") {
                throw new errors.realm.MissingParameters('token');
            }
            const token = Token_1.Token.parse(tokenData, this.publicKey);
            this.requireAdminOrUserId(req, token.identity);
            const expires = token.expires ? new Date(token.expires * 1000) : null;
            this.tokenRevocationRealm.write(() => {
                this.tokenRevocationRealm.create('TokenRevocation', {
                    expires,
                    token: token.encodedData(),
                    revoked: new Date(),
                }, true);
            });
            return {};
        });
    }
    getUserById(userId, req) {
        this.requireAdminOrUserId(req, userId);
        const user = this.adminRealm.objectForPrimaryKey('User', userId);
        if (!user) {
            throw new errors.realm.UnknownAccount();
        }
        return user;
    }
    getUserByProviderIdApi(provider, providerId, req) {
        const token = req.authToken;
        if (!token) {
            throw new errors.realm.AccessDenied();
        }
        const decodedProviderId = URI.decode(providerId);
        const user = this.getUserByProviderId(provider, decodedProviderId);
        if (user && (Token_2.isAdminToken(token) || user.userId === token.identity)) {
            return user;
        }
        throw new errors.realm.UnknownAccount();
    }
    createOrUpdateUserApi(req, providerId, provider, isAdmin, metadata = {}, userId) {
        return __awaiter(this, void 0, void 0, function* () {
            this.requireAdminOrUserId(req);
            return this.createOrUpdateUser(providerId, provider, isAdmin, metadata, userId);
        });
    }
    getProviders(req) {
        this.requireAdminOrUserId(req);
        this.requireRuntimeConfiguration();
        return this.providers.map(p => {
            let type;
            let config;
            let name;
            name = p.providerConfigName;
            const configObject = this.configurationRealm.objectForPrimaryKey('AuthProviderConfig', name);
            if (configObject) {
                type = configObject.type;
                config = configObject.config ? JSON.parse(configObject.config) : {};
            }
            return {
                name: name,
                type: type || 'unknown',
                config: config
            };
        });
    }
    addOrUpdateProviderApi(req, type, config, name) {
        return __awaiter(this, void 0, void 0, function* () {
            this.requireAdminOrUserId(req);
            this.requireRuntimeConfiguration();
            this.configurationRealm.write(() => {
                this.configurationRealm.create('AuthProviderConfig', {
                    name: name || type,
                    type: type,
                    config: config ? JSON.stringify(config) : null,
                    forceCodeConfig: false
                });
            });
            return {};
        });
    }
    removeProviderApi(req, name) {
        return __awaiter(this, void 0, void 0, function* () {
            this.requireAdminOrUserId(req);
            this.requireRuntimeConfiguration();
            this.configurationRealm.beginTransaction();
            const config = this.configurationRealm.objectForPrimaryKey('AuthProviderConfig', name);
            if (!config) {
                this.configurationRealm.cancelTransaction();
                throw new errors.JSONError({
                    status: 404,
                    title: 'Provider not found.',
                    detail: `The provider ${name} was not found in the list of active providers.`
                });
            }
            this.configurationRealm.delete(config);
            this.configurationRealm.commitTransaction();
            return {};
        });
    }
    updateProviderData(providerName, userId, data, req) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!req.authToken) {
                throw new errors.realm.AccessDenied();
            }
            if (!userId) {
                userId = req.authToken.identity;
            }
            this.requireAdminOrUserId(req, userId);
            const provider = this.providers.find((p) => p.name === providerName);
            if (!provider) {
                throw new errors.realm.InvalidParameters('provider');
            }
            if (!data) {
                throw new errors.realm.MissingParameters('data');
            }
            const user = this.adminRealm.objectForPrimaryKey('User', userId);
            if (!user) {
                throw new errors.realm.UnknownAccount();
            }
            if (provider.update) {
                return provider.update(user, data);
            }
            return {};
        });
    }
    updateProviderAccount(providerName, data, req, providerId) {
        return __awaiter(this, void 0, void 0, function* () {
            const provider = this.providers.find((p) => p.name === providerName);
            if (!provider) {
                throw new errors.realm.InvalidParameters('provider');
            }
            if (!provider.updateProviderAccount) {
                throw new errors.realm.InvalidParameters({ name: 'provider', reason: 'this provider does not support updating user accounts.' });
            }
            const user = providerId && this.getUserByProviderId(providerName, providerId);
            const token = req.authToken;
            const isAuthenticated = !!(token && (Token_2.isAdminToken(token) || (user && token.identity === user.userId)));
            const userAgent = useragent.parse(req.get('user-agent') || '');
            const ip = req.get('x-forwarded-for') || req.connection.remoteAddress;
            return yield provider.updateProviderAccount(providerId, user, data, isAuthenticated, userAgent, ip);
        });
    }
    deleteUser(userId, req) {
        return __awaiter(this, void 0, void 0, function* () {
            this.logger.debug(`User ${userId} is requested to be deleted`);
            this.requireAdminOrUserId(req, userId);
            yield this.deleteUserWithUserId(userId);
            this.logger.debug(`User ${userId} has been deleted`);
            return {};
        });
    }
    createProvider(config) {
        let parsedConfig;
        if (!config.config) {
            parsedConfig = {};
        }
        else {
            let configString = config.config;
            const matches = configString.match(/%%[^%]*%%/g);
            if (matches) {
                for (const match of matches) {
                    const replacement = process.env[match.replace(/%%/g, '')];
                    configString = configString.replace(match, replacement);
                }
            }
            parsedConfig = JSON.parse(configString);
        }
        const Providers = require('../auth');
        const result = new Providers[config.type](parsedConfig);
        result.providerConfigName = config.name;
        return result;
    }
    startProvider(provider) {
        return __awaiter(this, void 0, void 0, function* () {
            Reflect.set(provider, "service", this);
            if (provider.start) {
                yield provider.start();
            }
        });
    }
    stopProvider(provider) {
        return __awaiter(this, void 0, void 0, function* () {
            let toStop;
            if (typeof provider === 'string') {
                const index = this.providers.findIndex(p => p.providerConfigName === provider);
                if (index !== -1) {
                    toStop = this.providers.splice(index, 1)[0];
                }
            }
            else {
                toStop = provider;
            }
            if (toStop) {
                if (toStop.stop) {
                    yield toStop.stop();
                }
                Reflect.set(toStop, 'service', undefined);
            }
        });
    }
    pruneRevocationTokens() {
        const deadRevocations = this.tokenRevocationRealm.objects('TokenRevocation').filtered("expires < $0", new Date());
        if (deadRevocations.length > 0) {
            this.tokenRevocationRealm.write(() => {
                this.tokenRevocationRealm.delete(deadRevocations);
            });
        }
    }
    enqueueProviderOperation(operation) {
        this.providerQueue.add(() => __awaiter(this, void 0, void 0, function* () {
            try {
                this.hasPendingProviderOperations = true;
                yield operation();
            }
            catch (err) {
                this.logger.log('error', `Provider modification operation failed: ${err}`);
            }
            finally {
                this.hasPendingProviderOperations = false;
                this.providerQueue.next();
            }
        }));
    }
    stopProviderQueue() {
        return __awaiter(this, void 0, void 0, function* () {
            this.providerQueue.stop = true;
            yield util_1.waitAsync(() => this.hasPendingProviderOperations, p => p === false, 10000);
        });
    }
    generateUniqueId() {
        return crypto.randomBytes(16).toString('hex');
    }
    requireAdminOrUserId(req, userId) {
        const authToken = req.authToken;
        if (!authToken) {
            throw new errors.realm.AccessDenied();
        }
        const isAdmin = Token_2.isAdminToken(authToken);
        if (!isAdmin) {
            if (!userId) {
                throw new errors.realm.AccessDenied();
            }
            if (userId && userId !== authToken.identity) {
                throw new errors.realm.AccessDenied();
            }
        }
    }
    requireRuntimeConfiguration() {
        if (!this.runtimeConfigurationEnabled) {
            throw new errors.JSONError({
                title: 'Runtime configuration disabled',
                status: 403,
                detail: 'The runtime configuration API has been disabled.'
            });
        }
    }
    deleteUserWithUserId(userId) {
        return __awaiter(this, void 0, void 0, function* () {
            const adminRealm = this.adminRealm;
            const user = adminRealm.objectForPrimaryKey('User', userId);
            if (!user) {
                throw new errors.realm.UnknownUser();
            }
            const realmFiles = adminRealm.objects('RealmFile')
                .filtered('owner.userId = $0', userId);
            const filesToDelete = realmFiles.map((realmFile) => {
                return {
                    tags: [
                        'role=master',
                        `label=${realmFile.syncLabel}`
                    ],
                    path: realmFile.path
                };
            });
            let providerNames = [];
            adminRealm.write(() => {
                const user = adminRealm.objectForPrimaryKey('User', userId);
                if (!user) {
                    throw new errors.realm.UnknownUser();
                }
                adminRealm.delete(user.metadata);
                providerNames = user.accounts.map((account) => {
                    return account.provider;
                });
                adminRealm.delete(user.accounts);
                const permissions = adminRealm.objects('Permission')
                    .filtered('user.userId = $0', userId);
                adminRealm.delete(permissions);
                const realmFiles = adminRealm.objects('RealmFile')
                    .filtered('owner.userId = $0', userId);
                adminRealm.delete(realmFiles);
                adminRealm.delete(user);
            });
            const deleteUserPromises = [];
            providerNames.forEach((providerName) => {
                const provider = this.providers.find((p) => p.name === providerName);
                if (provider) {
                    deleteUserPromises.push(provider.deleteUser(userId));
                }
            });
            yield Promise.all(deleteUserPromises);
            yield Promise.all(filesToDelete.map((file) => __awaiter(this, void 0, void 0, function* () {
                yield this.server['realmFactory'].forceCloseRealm(file.path, `Force closing Realm at path ${file.path} due to user deletion of ${userId}.`, true);
                const client = new SyncClient_1.SyncClient(this.server.discovery, this.adminToken, this.server["serverConfig"].authorizationHeaderName, file.tags);
                yield client.deleteRealm(file.path).catch((err) => {
                    this.logger.error(`Deletion of a Realm on the sync server failed, path = ${file.path}`);
                });
            })));
            const localUserDir = path.join(this.server.dataPath, "realms", userId);
            fs.remove(localUserDir, (err) => {
                if (err) {
                    this.logger.error(`Unable to remove user directory: '${localUserDir}'`);
                }
            });
        });
    }
    static ensureRealmOfType(requiredType, actualType) {
        if (!(actualType in realms_1.RealmType)) {
            actualType = realms_1.RealmType.full;
        }
        if (actualType !== requiredType) {
            throw new errors.realm.InvalidRealmType(requiredType, actualType);
        }
    }
};
__decorate([
    decorators_1.Start(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Server_1.Server]),
    __metadata("design:returntype", Promise)
], AuthService.prototype, "start", null);
__decorate([
    decorators_1.Stopping(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], AuthService.prototype, "stopping", null);
__decorate([
    decorators_1.Stop(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], AuthService.prototype, "stop", null);
__decorate([
    decorators_1.Post('/'),
    __param(0, decorators_1.Body()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], AuthService.prototype, "authenticate", null);
__decorate([
    decorators_1.Post("/revoke"),
    __param(0, decorators_1.Body('token')),
    __param(1, decorators_1.Request()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], AuthService.prototype, "revokeToken", null);
__decorate([
    decorators_1.Get('/users/:user_id'),
    __param(0, decorators_1.Params('user_id')),
    __param(1, decorators_1.Request()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", realms_1.User)
], AuthService.prototype, "getUserById", null);
__decorate([
    decorators_1.Get('/users/:provider/:provider_id'),
    __param(0, decorators_1.Params('provider')),
    __param(1, decorators_1.Params('provider_id')),
    __param(2, decorators_1.Request()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String, Object]),
    __metadata("design:returntype", realms_1.User)
], AuthService.prototype, "getUserByProviderIdApi", null);
__decorate([
    decorators_1.Put('/users'),
    __param(0, decorators_1.Request()),
    __param(1, decorators_1.Body('provider_id')),
    __param(2, decorators_1.Body('provider')),
    __param(3, decorators_1.Body('is_admin')),
    __param(4, decorators_1.Body('metadata')),
    __param(5, decorators_1.Body('user_id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, String, String, Boolean, Object, String]),
    __metadata("design:returntype", Promise)
], AuthService.prototype, "createOrUpdateUserApi", null);
__decorate([
    decorators_1.Get('/providers'),
    __param(0, decorators_1.Request()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Array)
], AuthService.prototype, "getProviders", null);
__decorate([
    decorators_1.Post('/providers'),
    __param(0, decorators_1.Request()),
    __param(1, decorators_1.Body('type')),
    __param(2, decorators_1.Body('config')),
    __param(3, decorators_1.Body('name')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, String, Object, String]),
    __metadata("design:returntype", Promise)
], AuthService.prototype, "addOrUpdateProviderApi", null);
__decorate([
    decorators_1.Delete('/providers/:name'),
    __param(0, decorators_1.Request()),
    __param(1, decorators_1.Params('name')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, String]),
    __metadata("design:returntype", Promise)
], AuthService.prototype, "removeProviderApi", null);
__decorate([
    decorators_1.Put('/:provider'),
    __param(0, decorators_1.Params('provider')),
    __param(1, decorators_1.Body('user_id')),
    __param(2, decorators_1.Body('data')),
    __param(3, decorators_1.Request()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String, Object, Object]),
    __metadata("design:returntype", Promise)
], AuthService.prototype, "updateProviderData", null);
__decorate([
    decorators_1.Post('/:provider/updateAccount'),
    __param(0, decorators_1.Params('provider')),
    __param(1, decorators_1.Body('data')),
    __param(2, decorators_1.Request()),
    __param(3, decorators_1.Body('provider_id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object, Object, String]),
    __metadata("design:returntype", Promise)
], AuthService.prototype, "updateProviderAccount", null);
__decorate([
    decorators_1.Delete('/user/:user_id'),
    decorators_1.Delete('/users/:user_id'),
    __param(0, decorators_1.Params('user_id')),
    __param(1, decorators_1.Request()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], AuthService.prototype, "deleteUser", null);
AuthService = AuthService_1 = __decorate([
    decorators_1.BaseRoute('/auth'),
    decorators_1.ServiceName('auth'),
    decorators_1.Cors('/'),
    __metadata("design:paramtypes", [Object])
], AuthService);
exports.AuthService = AuthService;
var AuthService_1;
//# sourceMappingURL=AuthService.js.map