"use strict";
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 chai_1 = require("chai");
const superagent = require("superagent");
const uuid = require("uuid");
const TestServer_1 = require("../TestServer");
const Token_1 = require("../shared/Token");
const index_1 = require("../index");
const AdminRealm_1 = require("../realms/AdminRealm");
const uuid_1 = require("uuid");
describe('RealmDirectoryService Tests', function () {
    let server;
    let service;
    let adminToken;
    let adminRealm;
    let authService;
    before(() => __awaiter(this, void 0, void 0, function* () {
        server = new TestServer_1.TestServer();
        yield server.start({});
        service = server.getService('realms');
        authService = server.getService("auth");
        adminToken = Token_1.Token.parse(server.adminToken, server.publicKey);
        adminRealm = service['adminRealm'];
    }));
    after(() => __awaiter(this, void 0, void 0, function* () {
        yield server.shutdown().catch((err) => { });
    }));
    describe("state after start", () => {
        it('registered the internal realms used by ROS', () => __awaiter(this, void 0, void 0, function* () {
            chai_1.assert.isDefined(adminRealm.objectForPrimaryKey('RealmFile', '/__admin'));
        }));
    });
    describe("findByPath()", () => {
        describe("when a token is not provided", () => {
            it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
                const shouldCreate = 'false';
                yield chai_1.assert.isRejected(service['findByPath']("%2Fproducts", {}, shouldCreate), "The path is invalid or current user has no access.");
            }));
        });
        describe("when the realm does not exist", () => {
            const shouldCreate = 'true';
            const realmType = AdminRealm_1.RealmType.full;
            describe("and the token is an admin token", () => {
                it('creates a realm and returns it', () => __awaiter(this, void 0, void 0, function* () {
                    chai_1.expect(adminRealm.objectForPrimaryKey('RealmFile', '/products')).to.be.undefined;
                    const response = yield service['findByPath']("%2Fproducts", { authToken: adminToken }, shouldCreate, realmType);
                    chai_1.expect(response.path).to.equal('/products');
                    chai_1.expect(response.syncLabel).to.be.ok;
                    chai_1.expect(adminRealm.objectForPrimaryKey('RealmFile', '/products')).to.be.ok;
                }));
                it("creates a realm in a user's folder", () => __awaiter(this, void 0, void 0, function* () {
                    const user = yield authService.createOrUpdateUser(uuid.v4(), 'nickname', false);
                    const realmPath = `/${user.userId}/myrealm`;
                    chai_1.expect(adminRealm.objectForPrimaryKey('RealmFile', realmPath)).to.be.undefined;
                    const response = yield service['findByPath'](realmPath, { authToken: adminToken }, shouldCreate, realmType);
                    chai_1.expect(response.path).to.equal(realmPath);
                    chai_1.expect(response.syncLabel).to.be.ok;
                    chai_1.expect(adminRealm.objectForPrimaryKey('RealmFile', realmPath)).to.be.ok;
                }));
                for (const useTilde of [true, false]) {
                    it(`grants correct permissions when ownerId is provided and ~ is ${useTilde ? '' : 'not '}used`, () => __awaiter(this, void 0, void 0, function* () {
                        const user = yield authService.createOrUpdateUser(uuid.v4(), 'nickname', false);
                        const realmPath = `/${user.userId}/myrealm`;
                        chai_1.expect(adminRealm.objectForPrimaryKey('RealmFile', realmPath)).to.be.undefined;
                        const pathToFind = useTilde ? realmPath.replace(user.userId, '~') : realmPath;
                        const response = yield service['findByPath'](pathToFind, { authToken: adminToken }, shouldCreate, realmType, user.userId);
                        chai_1.expect(response.path).to.equal(realmPath);
                        chai_1.expect(response.syncLabel).to.be.ok;
                        const realmFile = adminRealm.objectForPrimaryKey('RealmFile', realmPath);
                        chai_1.expect(realmFile).to.be.ok;
                        chai_1.expect(realmFile.owner.userId).to.equal(user.userId);
                        const permissions = adminRealm.objects('Permission').filtered('realmFile = $0', realmFile);
                        chai_1.expect(permissions.length).to.equal(1);
                        chai_1.expect(permissions[0].user.userId).to.equal(user.userId);
                        chai_1.expect(permissions[0].mayRead).to.be.true;
                        chai_1.expect(permissions[0].mayWrite).to.be.true;
                        chai_1.expect(permissions[0].mayManage).to.be.true;
                    }));
                }
            });
            describe('and the user is not an admin', () => {
                const shouldCreate = 'true';
                const realmType = AdminRealm_1.RealmType.full;
                const assertPermissions = (realmFile, userId) => {
                    const permissions = adminRealm.objects('Permission').filtered('realmFile = $0 && user.userId = $1', realmFile, userId);
                    chai_1.expect(permissions.length).to.equal(1);
                    chai_1.expect(permissions[0].mayRead).to.true;
                    chai_1.expect(permissions[0].mayWrite).to.true;
                    chai_1.expect(permissions[0].mayManage).to.true;
                };
                for (const useTilde of [true, false]) {
                    it(`creates a realm and returns it when ~ is ${useTilde ? '' : 'not '}used`, () => __awaiter(this, void 0, void 0, function* () {
                        const user = yield authService.createOrUpdateUser(uuid.v4(), 'nickname', false);
                        const realmPath = `/${user.userId}/myrealm`;
                        chai_1.expect(adminRealm.objectForPrimaryKey('RealmFile', realmPath)).to.be.undefined;
                        const pathToFind = useTilde ? realmPath.replace(user.userId, '~') : realmPath;
                        const response = yield service['findByPath'](pathToFind, { authToken: {
                                identity: user.userId,
                            } }, shouldCreate, realmType);
                        chai_1.expect(response.path).to.equal(realmPath);
                        chai_1.expect(response.syncLabel).to.be.ok;
                        const realmFile = adminRealm.objectForPrimaryKey('RealmFile', realmPath);
                        chai_1.expect(realmFile).to.be.ok;
                        chai_1.expect(realmFile.owner.userId).to.equal(user.userId);
                        assertPermissions(realmFile, user.userId);
                    }));
                }
                it("fails to create realm in other user's folder", () => __awaiter(this, void 0, void 0, function* () {
                    const joe = yield authService.createOrUpdateUser(uuid.v4(), 'nickname', false);
                    const bob = yield authService.createOrUpdateUser(uuid.v4(), 'nickname', false);
                    const realmPath = `/${bob.userId}/myrealm`;
                    chai_1.assert.isUndefined(adminRealm.objectForPrimaryKey('RealmFile', realmPath));
                    const request = {
                        authToken: {
                            identity: joe.userId
                        }
                    };
                    yield chai_1.assert.isRejected(service['findByPath'](realmPath, request, shouldCreate, realmType), 'The path is invalid or current user has no access.');
                }));
                it('fails to impersonate other user', () => __awaiter(this, void 0, void 0, function* () {
                    const joe = yield authService.createOrUpdateUser(uuid.v4(), 'nickname', false);
                    const bob = yield authService.createOrUpdateUser(uuid.v4(), 'nickname', false);
                    const joePath = `/${joe.userId}/myrealm`;
                    const bobPath = `/${bob.userId}/myrealm`;
                    chai_1.assert.isUndefined(adminRealm.objectForPrimaryKey('RealmFile', joePath));
                    chai_1.assert.isUndefined(adminRealm.objectForPrimaryKey('RealmFile', bobPath));
                    const request = {
                        authToken: {
                            identity: joe.userId
                        }
                    };
                    yield chai_1.assert.isRejected(service['findByPath'](joePath, request, shouldCreate, realmType, bob.userId), 'The path is invalid or current user has no access.');
                    yield chai_1.assert.isRejected(service['findByPath'](bobPath, request, shouldCreate, realmType, bob.userId), 'The path is invalid or current user has no access.');
                }));
                it('successfully impersonates themselves', () => __awaiter(this, void 0, void 0, function* () {
                    const user = yield authService.createOrUpdateUser(uuid.v4(), 'nickname', false);
                    const realmPath = `/${user.userId}/myrealm`;
                    chai_1.assert.isUndefined(adminRealm.objectForPrimaryKey('RealmFile', realmPath));
                    const response = yield service['findByPath'](realmPath, { authToken: {
                            identity: user.userId,
                        } }, shouldCreate, realmType, user.userId);
                    chai_1.assert.equal(response.path, realmPath);
                    chai_1.assert.isDefined(response.syncLabel);
                    const realmFile = adminRealm.objectForPrimaryKey('RealmFile', realmPath);
                    chai_1.expect(realmFile).to.be.ok;
                    chai_1.expect(realmFile.owner.userId).to.equal(user.userId);
                    assertPermissions(realmFile, user.userId);
                }));
            });
            describe("and path is invalid", () => {
                const shouldCreate = 'true';
                const realmType = AdminRealm_1.RealmType.full;
                for (const p of ['myrealm.realm', 'cæt', '.hello']) {
                    it(`should reject /${p} with an error`, () => __awaiter(this, void 0, void 0, function* () {
                        const user = yield authService.createOrUpdateUser(uuid.v4(), 'nickname', false);
                        const realmPath = `/${user.userId}/${p}`;
                        yield chai_1.assert.isRejected(service['findByPath'](realmPath, { authToken: {
                                identity: user.userId,
                            } }, shouldCreate, realmType), "path is invalid");
                        chai_1.assert.isUndefined(adminRealm.objectForPrimaryKey('RealmFile', realmPath));
                    }));
                }
            });
        });
    });
    describe("remove()", () => {
        let revokedToken;
        let userToken;
        let adminToken;
        before(() => __awaiter(this, void 0, void 0, function* () {
            revokedToken = new Token_1.RefreshToken({
                isAdmin: false,
                identity: "revoked-identity",
                appId: "some.appId.com",
            });
            userToken = new Token_1.RefreshToken({
                isAdmin: false,
                identity: "user-identity",
                appId: "some.appId.com",
                expires: (Date.now() / 1000) + 60,
            });
            adminToken = new Token_1.RefreshToken({
                isAdmin: true,
                identity: "admin-identity",
                appId: "some.appId.com",
            });
            yield superagent.post(`${server.url}/auth/revoke`).set({
                Authorization: adminToken.sign(server.privateKey),
            }).send({
                token: userToken.sign(server.privateKey),
            });
        }));
        describe("when a token is not provided", () => {
            it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
                yield chai_1.assert.isRejected(service['remove']("/some-path", {}), "The path is invalid or current user has no access.");
            }));
        });
        describe("when a revoked token is provided", () => {
            it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
                yield chai_1.assert.isRejected(service['remove']("/some-path", { authToken: revokedToken }), "The path is invalid or current user has no access.");
            }));
        });
        describe("when a regular user refresh token is provided", () => {
            it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
                yield chai_1.assert.isRejected(service['remove']("/some-path", { authToken: userToken }), "The path is invalid or current user has no access.");
            }));
        });
        describe("when the realm file is not found", () => {
            it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
                yield chai_1.assert.isRejected(service['remove']("/some-path", { authToken: adminToken }), "The Realm could not be found");
            }));
        });
        describe("when the sync worker is unavailable", () => {
            it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
                yield service['findOrCreateRealmFile']("/file-to-delete", null, AdminRealm_1.RealmType.full, "otherLabel");
                yield chai_1.assert.isRejected(service['remove']("/file-to-delete", { authToken: adminToken }), "The requested service is temporarily unavailable.");
            }));
        });
        describe("when the sync worker is available", () => {
            const realmType = AdminRealm_1.RealmType.full;
            it("should remove the realm", () => __awaiter(this, void 0, void 0, function* () {
                yield service['findOrCreateRealmFile']("/file-to-delete2", null, realmType);
                yield chai_1.assert.isFulfilled(service['remove']("/file-to-delete2", { authToken: adminToken }));
            }));
            it("should not remove the realm - if protected", () => __awaiter(this, void 0, void 0, function* () {
                const protectedRealmPaths = [
                    '/__admin',
                    '/__revocation',
                    '/__wildcardpermissions',
                    '/__password',
                    '/__perm',
                    '/__configuration'
                ];
                const assertFailureWhenDeletingRealm = (path) => __awaiter(this, void 0, void 0, function* () {
                    yield chai_1.assert.isRejected(service['remove'](path, { authToken: adminToken }), "This Realm is protected from removal");
                });
                for (const path of protectedRealmPaths) {
                    yield service["findOrCreateRealmFile"](path, null, realmType);
                    yield assertFailureWhenDeletingRealm(path);
                }
                ;
            }));
        });
    });
    describe("changeType", () => {
        const assertError = (promise, error) => __awaiter(this, void 0, void 0, function* () {
            const actual = yield chai_1.assert.isRejected(promise);
            chai_1.assert.equal(actual.status, error.status);
            chai_1.assert.equal(actual.code, error.code);
        });
        const assertType = (path, type) => __awaiter(this, void 0, void 0, function* () {
            const shouldCreate = 'false';
            const result = yield service['findByPath'](path, { authToken: adminToken }, shouldCreate);
            chai_1.assert.equal(result.realmType, type);
        });
        describe("when a token is not provided", () => {
            it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
                yield assertError(service['changeType']("%2Fproducts", AdminRealm_1.RealmType.full, {}), new index_1.errors.realm.AccessDenied());
            }));
        });
        describe("when token is not admin token", () => {
            it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
                const nonAdminToken = new Token_1.RefreshToken({
                    isAdmin: false,
                    appId: '',
                    identity: uuid_1.v4()
                });
                yield assertError(service['changeType']("%2Fproducts", AdminRealm_1.RealmType.full, { authToken: nonAdminToken }), new index_1.errors.realm.AccessDenied());
            }));
        });
        describe("when realmType is not provided", () => {
            it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
                yield assertError(service['changeType']("%2Fproducts", undefined, { authToken: adminToken }), new index_1.errors.realm.InvalidParameters());
            }));
        });
        describe("when realmType is invalid", () => {
            it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
                yield assertError(service['changeType']("%2Fproducts", 'foo', { authToken: adminToken }), new index_1.errors.realm.InvalidParameters());
            }));
        });
        describe("when realmPath doesn't exist", () => {
            it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
                yield assertError(service['changeType'](`%2F${uuid_1.v4()}`, AdminRealm_1.RealmType.full, { authToken: adminToken }), new index_1.errors.realm.RealmNotFound());
            }));
        });
        describe("when realmPath exists", () => {
            describe("and type is already the desired", () => __awaiter(this, void 0, void 0, function* () {
                it("should succeed", () => __awaiter(this, void 0, void 0, function* () {
                    const path = `%2F${uuid_1.v4()}`;
                    const shouldCreate = 'true';
                    const realmType = AdminRealm_1.RealmType.full;
                    yield service['findByPath'](path, { authToken: adminToken }, shouldCreate, realmType);
                    yield chai_1.assert.isFulfilled(service['changeType'](path, realmType, { authToken: adminToken }));
                    yield assertType(path, AdminRealm_1.RealmType.full);
                }));
            }));
            describe("and type needs to change", () => __awaiter(this, void 0, void 0, function* () {
                it("should succeed", () => __awaiter(this, void 0, void 0, function* () {
                    const path = `%2F${uuid_1.v4()}`;
                    const shouldCreate = 'true';
                    const realmType = AdminRealm_1.RealmType.full;
                    yield service['findByPath'](path, { authToken: adminToken }, shouldCreate, realmType);
                    yield chai_1.assert.isFulfilled(service['changeType'](path, AdminRealm_1.RealmType.reference, { authToken: adminToken }));
                    yield assertType(path, AdminRealm_1.RealmType.reference);
                }));
            }));
        });
    });
});
//# sourceMappingURL=RealmDirectoryService.spec.js.map