realm-object-server
Version:
785 lines • 55.3 kB
JavaScript
"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 chai = require("chai");
chai.use(require("chai-as-promised"));
const sinon = require("sinon");
const faker = require("faker");
const uuid = require("uuid");
const TestServer_1 = require("../TestServer");
const Token_1 = require("../shared/Token");
const auth_1 = require("../auth");
const errors = require("../errors");
const index_1 = require("../shared/index");
const index_2 = require("../index");
const realms_1 = require("../realms");
describe("AuthService", () => {
describe("build time API", () => {
let server;
let service;
let provider;
let passwordProvider;
let passwordEmailHandler;
let adminAuthToken;
before(() => __awaiter(this, void 0, void 0, function* () {
server = new TestServer_1.TestServer();
provider = new auth_1.NicknameAuthProvider();
passwordEmailHandler = {
resetPassword: (email, token, userAgent, remoteIp) => __awaiter(this, void 0, void 0, function* () { })
};
passwordProvider = new auth_1.PasswordAuthProvider({
emailHandler: passwordEmailHandler
});
yield server.start({
authProviders: [provider, passwordProvider]
});
service = server.getService("auth");
adminAuthToken = server.tokenValidator.parse(server.adminToken);
}));
after(() => __awaiter(this, void 0, void 0, function* () {
yield server.shutdown();
}));
describe("createOrUpdateUser", () => {
describe("without a provider ID and provider", () => {
it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
const err = yield chai_1.assert.isRejected(service.createOrUpdateUser(undefined, undefined, false), errors.realm.MissingParameters);
chai_1.assert.includeDeepMembers(err.invalidParams, [{
name: "providerId",
reason: "Missing parameter 'providerId'!",
}, {
name: "provider",
reason: "Missing parameter 'provider'!",
}]);
}));
});
describe("without a provider", () => {
it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
const err = yield chai_1.assert.isRejected(service.createOrUpdateUser("some-user", undefined, false), errors.realm.MissingParameters);
chai_1.assert.includeDeepMembers(err.invalidParams, [{
name: "provider",
reason: "Missing parameter 'provider'!",
}]);
}));
});
describe("without a providerId", () => {
it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
const err = yield chai_1.assert.isRejected(service.createOrUpdateUser(undefined, "nickname", false), errors.realm.MissingParameters);
chai_1.assert.includeDeepMembers(err.invalidParams, [{
name: "providerId",
reason: "Missing parameter 'providerId'!",
}]);
}));
});
describe("with a userId that requires URL encoding", () => {
it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
const err = yield chai_1.assert.isRejected(service.createOrUpdateUser("some-user", "nickname", false, {}, "some/user"), errors.realm.InvalidParameters);
chai_1.assert.includeDeepMembers(err.invalidParams, [{
name: "userId",
reason: "Realm requires the userId that does not require URI encoding. Supplied: 'some/user', encoded: 'some%2Fuser'",
}]);
}));
});
describe("when the user already exists", () => {
let user;
let username;
beforeEach(() => __awaiter(this, void 0, void 0, function* () {
username = faker.internet.userName();
user = yield service.createOrUpdateUser(username, "nickname", false);
}));
it("without userId should not create a new user", () => __awaiter(this, void 0, void 0, function* () {
const result = yield chai_1.assert.isFulfilled(service.createOrUpdateUser(username, "nickname", false));
chai_1.assert.isFalse(result.created);
}));
it("with userId should not create a new user", () => __awaiter(this, void 0, void 0, function* () {
const result = yield chai_1.assert.isFulfilled(service.createOrUpdateUser(username, "nickname", false, {}, user.userId));
chai_1.assert.isFalse(result.created);
}));
it("with incorrect userId should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
yield chai_1.assert.isRejected(service.createOrUpdateUser(username, "nickname", false, {}, "some-other-userid"), errors.realm.InvalidParameters);
}));
describe("with different provider ids but the same userId", () => {
it("when providers are the same should have two accounts", () => __awaiter(this, void 0, void 0, function* () {
const firstProviderId = faker.internet.userName();
const userId = uuid.v4();
yield chai_1.assert.isFulfilled(service.createOrUpdateUser(firstProviderId, "nickname", false, {}, userId));
const secondProviderId = faker.internet.userName();
const result = yield chai_1.assert.isFulfilled(service.createOrUpdateUser(secondProviderId, "nickname", false, {}, userId));
chai_1.assert.isTrue(result.created);
chai_1.assert.equal(result.accounts.length, 2);
chai_1.expect(result.accounts.map(a => {
return {
providerId: a.providerId,
provider: a.provider,
};
})).to.eql([
{ providerId: firstProviderId, provider: "nickname" },
{ providerId: secondProviderId, provider: "nickname" },
]);
}));
it("when providers are different should have two accounts", () => __awaiter(this, void 0, void 0, function* () {
const nicknameId = faker.internet.userName();
const userId = uuid.v4();
yield chai_1.assert.isFulfilled(service.createOrUpdateUser(nicknameId, "nickname", false, {}, userId));
const secondProviderId = faker.internet.userName();
const result = yield chai_1.assert.isFulfilled(service.createOrUpdateUser(secondProviderId, "anonymous", false, {}, userId));
chai_1.assert.isTrue(result.created);
chai_1.assert.equal(result.accounts.length, 2);
chai_1.expect(result.accounts.map(a => {
return {
providerId: a.providerId,
provider: a.provider,
};
})).to.eql([
{ providerId: nicknameId, provider: "nickname" },
{ providerId: secondProviderId, provider: "anonymous" },
]);
}));
it("when the user role changes should update the user", () => __awaiter(this, void 0, void 0, function* () {
const firstProviderId = faker.internet.userName();
const userId = uuid.v4();
yield chai_1.assert.isFulfilled(service.createOrUpdateUser(firstProviderId, "nickname", false, {}, userId));
const secondProviderId = faker.internet.userName();
const result = yield chai_1.assert.isFulfilled(service.createOrUpdateUser(secondProviderId, "nickname", true, {}, userId));
chai_1.assert.isTrue(result.created);
chai_1.assert.equal(result.accounts.length, 2);
chai_1.assert.isTrue(result.isAdmin);
}));
});
});
describe("when the user does not already exist", () => {
it("without userId should create a new user", () => __awaiter(this, void 0, void 0, function* () {
const result = yield chai_1.assert.isFulfilled(service.createOrUpdateUser(faker.internet.userName(), "nickname", false));
chai_1.assert.isTrue(result.created);
}));
it("with userId should create a new user with the userId", () => __awaiter(this, void 0, void 0, function* () {
const username = faker.internet.userName();
const result = yield chai_1.assert.isFulfilled(service.createOrUpdateUser(username, "nickname", false, {}, "some-userid"));
chai_1.assert.equal(result.userId, "some-userid");
chai_1.assert.isTrue(result.created);
chai_1.assert.equal(result.accounts[0].provider, "nickname");
chai_1.assert.equal(result.accounts[0].providerId, username);
}));
});
});
describe("getUserById", () => {
describe("when the user does not exist", () => {
it("should not return a user", () => __awaiter(this, void 0, void 0, function* () {
try {
yield server.authClient.getUserById("some-userId");
chai_1.assert.fail();
}
catch (e) {
const expected = new errors.realm.UnknownAccount();
chai_1.expect(e.status).to.equal(expected.status);
chai_1.expect(e.code).to.equal(expected.code);
chai_1.expect(e.title).to.equal(expected.title);
}
}));
});
describe("when the user exists", () => {
let user;
let username;
before(() => __awaiter(this, void 0, void 0, function* () {
username = faker.internet.userName();
user = yield service.createOrUpdateUser(username, "nickname", false);
}));
it("should return the user", () => __awaiter(this, void 0, void 0, function* () {
const foundUser = yield server.authClient.getUserById(user.userId);
chai_1.assert.isDefined(foundUser);
chai_1.assert.equal(foundUser.userId, user.userId);
chai_1.assert.isFalse(foundUser.isAdmin);
chai_1.assert.equal(foundUser.accounts[0].provider, "nickname");
chai_1.assert.equal(foundUser.accounts[0].providerId, username);
}));
});
});
describe("getUserByProviderId", () => {
describe("when the user exists", () => {
let username;
before(() => __awaiter(this, void 0, void 0, function* () {
username = faker.internet.userName();
yield service.createOrUpdateUser(username, "nickname", false);
}));
it("should be able to get a user", () => __awaiter(this, void 0, void 0, function* () {
const fetchedUser = service.getUserByProviderId("nickname", username);
chai_1.assert.isDefined(fetchedUser);
chai_1.assert.isDefined(fetchedUser.userId);
chai_1.assert.isFalse(fetchedUser.isAdmin);
chai_1.assert.equal(fetchedUser.accounts[0].provider, "nickname");
chai_1.assert.equal(fetchedUser.accounts[0].providerId, username);
}));
});
describe("when the user does not exist", () => {
it("should be able to get a user", () => __awaiter(this, void 0, void 0, function* () {
const fetchedUser = service.getUserByProviderId("nickname", "some-user");
chai_1.assert.isUndefined(fetchedUser);
}));
});
});
describe("updateProviderData", () => {
describe("with an existing user", () => {
let user;
before(() => __awaiter(this, void 0, void 0, function* () {
const username = faker.internet.userName();
user = yield service.createOrUpdateUser(username, "nickname", false);
}));
it("should call the provider's update function", () => __awaiter(this, void 0, void 0, function* () {
const spy = sinon.spy(provider, "update");
const updateData = { debuggy: "mcbuggerson" };
yield chai_1.assert.isFulfilled(server.authClient.updateProviderData("nickname", user.userId, updateData));
sinon.assert.calledOnce(spy);
const args = spy.getCall(0).args;
chai_1.expect(args[0].userId).to.equal(user.userId);
chai_1.expect(args[1]).to.deep.equal(updateData);
}));
});
describe("without an existing user", () => {
it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
try {
yield server.authClient.updateProviderData("nickname", "some-bogus-userId", {});
chai_1.assert.fail();
}
catch (e) {
const expected = new errors.realm.UnknownAccount();
chai_1.expect(e.status).to.equal(expected.status);
chai_1.expect(e.code).to.equal(expected.code);
chai_1.expect(e.title).to.equal(expected.title);
}
}));
});
describe("without a valid provider", () => {
it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
try {
yield server.authClient.updateProviderData("snapchat", "lololol", {});
chai_1.assert.fail();
}
catch (e) {
const expected = new errors.realm.InvalidParameters();
chai_1.expect(e.status).to.equal(expected.status);
chai_1.expect(e.code).to.equal(expected.code);
chai_1.expect(e.title).to.equal(expected.title);
chai_1.expect(e.invalidParams).to.include.deep.members([{
name: "provider",
reason: "Invalid parameter 'provider'!"
}]);
}
}));
});
});
describe("deleteUser", () => {
describe("without an existing user", () => {
it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
try {
yield server.authClient.deleteUser("some-bogus-userId");
chai_1.assert.fail();
}
catch (e) {
const expected = new errors.realm.UnknownUser();
chai_1.expect(e.status).to.equal(expected.status);
chai_1.expect(e.code).to.equal(expected.code);
chai_1.expect(e.title).to.equal(expected.title);
}
}));
});
describe("Delete a user twice", () => {
let userId;
before(() => __awaiter(this, void 0, void 0, function* () {
const username = faker.internet.userName();
const user = yield service.createOrUpdateUser(username, "nickname", false);
userId = user.userId;
}));
it("should accept", () => __awaiter(this, void 0, void 0, function* () {
yield chai_1.assert.becomes(service["deleteUser"](userId, { authToken: adminAuthToken }), {});
}));
it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
try {
yield server.authClient.deleteUser(userId);
chai_1.assert.fail();
}
catch (e) {
const expected = new errors.realm.UnknownUser();
chai_1.expect(e.status).to.equal(expected.status);
chai_1.expect(e.code).to.equal(expected.code);
chai_1.expect(e.title).to.equal(expected.title);
}
}));
});
describe("when a user Realm is open by RealmFactory", () => {
it("should close it", () => __awaiter(this, void 0, void 0, function* () {
const userId = uuid.v4();
const user = yield service.createOrUpdateUser(userId, "nickname", false, undefined, userId);
const realmPath = `/${user.userId}/foo`;
const realmDefinition = {
remotePath: realmPath,
schema: [{
name: "Foo",
properties: {
bar: "int"
}
}],
syncLabel: "default"
};
yield server.ensureRealmExists(realmPath, userId);
const preDeleteRealm = yield server.openRealm(realmDefinition);
chai_1.assert.isFalse(preDeleteRealm.isClosed);
preDeleteRealm.write(() => {
preDeleteRealm.create("Foo", {
bar: 1
});
});
chai_1.assert.equal(preDeleteRealm.objects("Foo").length, 1);
yield chai_1.assert.isFulfilled(service["deleteUser"](userId, { authToken: adminAuthToken }));
chai_1.assert.isTrue(preDeleteRealm.isClosed);
const postDeleteRealm = yield server.openRealm(realmDefinition);
chai_1.assert.isFalse(postDeleteRealm.isClosed);
chai_1.assert.notEqual(preDeleteRealm, postDeleteRealm);
chai_1.assert.equal(postDeleteRealm.objects("Foo").length, 0);
}));
});
});
describe("revoke", () => {
it("revokes tokens with no expiration", () => __awaiter(this, void 0, void 0, function* () {
const token = index_1.generateAdminToken({
privateKey: server.privateKey
});
chai_1.expect(() => server.tokenValidator.parse(token)).to.not.throw;
yield server.authClient.revokeToken(token);
chai_1.expect(() => server.tokenValidator.parse(token)).to.throw(errors.realm.AccessDenied);
}));
it("revokes tokens with expiration", () => __awaiter(this, void 0, void 0, function* () {
const token = index_1.generateAdminToken({
privateKey: server.privateKey,
expires: new Date().getTime() / 1000 + 10000
});
chai_1.expect(() => server.tokenValidator.parse(token)).to.not.throw;
yield server.authClient.revokeToken(token);
chai_1.expect(() => server.tokenValidator.parse(token)).to.throw(errors.realm.AccessDenied);
}));
});
describe("prune", () => {
let tokenRevocationRealm;
before(() => __awaiter(this, void 0, void 0, function* () {
tokenRevocationRealm = yield server.openRealm(index_2.TokenRevocationRealm);
}));
after(() => {
tokenRevocationRealm.close();
});
it("prunes expired tokens", () => __awaiter(this, void 0, void 0, function* () {
const token = index_1.generateAdminToken({
privateKey: server.privateKey,
expires: new Date().getTime() / 1000 + 1
});
const tokenId = server.tokenValidator.parse(token).tokenId;
yield server.authClient.revokeToken(token);
let revocation = tokenRevocationRealm.objectForPrimaryKey("TokenRevocation", tokenId);
chai_1.expect(revocation).to.be.ok;
yield index_1.delay(1100);
yield service["pruneRevocationTokens"]();
revocation = tokenRevocationRealm.objectForPrimaryKey("TokenRevocation", tokenId);
chai_1.expect(revocation).to.be.undefined;
}));
it("doesn't prune non-expiring tokens", () => __awaiter(this, void 0, void 0, function* () {
const noExpirationToken = index_1.generateAdminToken({
privateKey: server.privateKey
});
const futureExpirationToken = index_1.generateAdminToken({
privateKey: server.privateKey,
expires: new Date().getTime() / 1000 + 10000
});
const noExpirationTokenId = server.tokenValidator.parse(noExpirationToken).tokenId;
const futureExpirationTokenId = server.tokenValidator.parse(futureExpirationToken).tokenId;
yield server.authClient.revokeToken(noExpirationToken);
yield server.authClient.revokeToken(futureExpirationToken);
let noExpirationRevocation = tokenRevocationRealm.objectForPrimaryKey("TokenRevocation", noExpirationTokenId);
let futureExpirationRevocation = tokenRevocationRealm.objectForPrimaryKey("TokenRevocation", futureExpirationTokenId);
chai_1.expect(noExpirationRevocation).to.be.ok;
chai_1.expect(futureExpirationRevocation).to.be.ok;
yield service["pruneRevocationTokens"]();
noExpirationRevocation = tokenRevocationRealm.objectForPrimaryKey("TokenRevocation", noExpirationTokenId);
futureExpirationRevocation = tokenRevocationRealm.objectForPrimaryKey("TokenRevocation", futureExpirationTokenId);
chai_1.expect(noExpirationRevocation).to.be.ok;
chai_1.expect(futureExpirationRevocation).to.be.ok;
}));
});
describe("updateProviderAccount", () => {
describe("when provider supports updating", () => {
let user;
let username;
beforeEach(() => __awaiter(this, void 0, void 0, function* () {
provider.updateProviderAccount = (providerId, user, data, isAuthenticated, userAgent, remoteIp) => __awaiter(this, void 0, void 0, function* () {
return {};
});
username = faker.internet.userName();
user = yield service.createOrUpdateUser(username, "nickname", false);
}));
const updateAccount = (updateData, providerId, token = null) => __awaiter(this, void 0, void 0, function* () {
const spy = sinon.spy(provider, "updateProviderAccount");
yield chai_1.assert.isFulfilled(server.authClient.updateProviderAccount("nickname", updateData, providerId, token));
sinon.assert.calledOnce(spy);
spy.restore();
return spy.getCall(0).args;
});
describe("when providerId provided", () => {
it("passes the user", () => __awaiter(this, void 0, void 0, function* () {
const updateData = { debuggy: "mcbuggerson" };
const args = yield updateAccount(updateData, username);
chai_1.assert.equal(args[0], username);
chai_1.assert.equal(args[1].userId, user.userId);
chai_1.assert.deepEqual(args[2], updateData);
chai_1.assert.isFalse(args[3]);
}));
describe("when token provided", () => {
it("passes isAuthenticated: true", () => __awaiter(this, void 0, void 0, function* () {
const updateData = { debuggy: "mcbuggerson" };
const args = yield updateAccount(updateData, username, server.adminToken);
chai_1.assert.equal(args[0], username);
chai_1.assert.equal(args[1].userId, user.userId);
chai_1.assert.deepEqual(args[2], updateData);
chai_1.assert.isTrue(args[3]);
}));
});
});
describe("when providerId not provided", () => {
it("passes user: null", () => __awaiter(this, void 0, void 0, function* () {
const updateData = { debuggy: "mcbuggerson" };
const args = yield updateAccount(updateData, null);
chai_1.assert.isNull(args[0]);
chai_1.assert.isNull(args[1]);
chai_1.assert.deepEqual(args[2], updateData);
chai_1.assert.isFalse(args[3]);
}));
describe("when token provided", () => {
it("passes isAuthenticated: true", () => __awaiter(this, void 0, void 0, function* () {
const updateData = { debuggy: "mcbuggerson" };
const args = yield updateAccount(updateData, null, server.adminToken);
chai_1.assert.isNull(args[0]);
chai_1.assert.isNull(args[1]);
chai_1.assert.deepEqual(args[2], updateData);
chai_1.assert.isTrue(args[3]);
}));
});
});
});
describe("without a valid provider", () => {
it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
const error = yield chai_1.assert.isRejected(server.authClient.updateProviderAccount("snapchat", {}));
const expected = new errors.realm.InvalidParameters();
chai_1.expect(error.status).to.equal(expected.status);
chai_1.expect(error.code).to.equal(expected.code);
chai_1.expect(error.title).to.equal(expected.title);
chai_1.expect(error.invalidParams).to.include.deep.members([{
name: "provider",
reason: "Invalid parameter 'provider'!"
}]);
}));
});
describe("when provider doesn't support updating", () => {
it("should reject with an error", () => __awaiter(this, void 0, void 0, function* () {
provider.updateProviderAccount = undefined;
const error = yield chai_1.assert.isRejected(server.authClient.updateProviderAccount("nickname", {}));
const expected = new errors.realm.InvalidParameters();
chai_1.expect(error.status).to.equal(expected.status);
chai_1.expect(error.code).to.equal(expected.code);
chai_1.expect(error.title).to.equal(expected.title);
chai_1.expect(error.invalidParams).to.include.deep.members([{
name: "provider",
reason: "this provider does not support updating user accounts."
}]);
}));
});
});
describe("reset password flow", () => {
let user;
let username;
let password;
let spy;
let emailSpy;
beforeEach(() => __awaiter(this, void 0, void 0, function* () {
spy = sinon.spy(passwordProvider, "updateProviderAccount");
emailSpy = sinon.spy(passwordEmailHandler, "resetPassword");
username = faker.internet.email();
password = faker.internet.password();
user = yield passwordProvider.authenticateOrCreateUser({
username,
password,
register: true
});
}));
afterEach(() => {
spy.restore();
emailSpy.restore();
});
describe("when password reset", () => {
describe("and username is valid", () => {
it("calls passwordProvider.updateProviderAccount and email sender", () => __awaiter(this, void 0, void 0, function* () {
yield chai_1.assert.isFulfilled(server.authClient.requestPasswordReset(username));
sinon.assert.calledOnce(spy);
const args = spy.getCall(0).args;
chai_1.assert.equal(args[0], username);
chai_1.assert.equal(args[1].userId, user.userId);
chai_1.assert.isFalse(args[3]);
sinon.assert.calledOnce(emailSpy);
const emailArgs = emailSpy.getCall(0).args;
chai_1.assert.equal(emailArgs[0], username);
}));
});
describe("and username is invalid", () => {
it("calls passwordProvider.updateProviderAccount but not email sender", () => __awaiter(this, void 0, void 0, function* () {
yield chai_1.assert.isFulfilled(server.authClient.requestPasswordReset("some-non-existent-name"));
sinon.assert.calledOnce(spy);
const args = spy.getCall(0).args;
chai_1.assert.equal(args[0], "some-non-existent-name");
chai_1.assert.isUndefined(args[1]);
chai_1.assert.isFalse(args[3]);
sinon.assert.notCalled(emailSpy);
}));
});
});
describe("when completePasswordReset", () => {
describe("and token is valid", () => {
it("resets the user's password", () => __awaiter(this, void 0, void 0, function* () {
yield chai_1.assert.isFulfilled(server.authClient.requestPasswordReset(username));
sinon.assert.calledOnce(emailSpy);
const token = emailSpy.getCall(0).args[1];
yield chai_1.assert.isFulfilled(server.authClient.completePasswordReset(token, "new_password"));
const error = yield chai_1.assert.isRejected(passwordProvider.authenticateOrCreateUser({
username,
password,
register: false
}));
const expected = new errors.realm.InvalidCredentials();
chai_1.assert.equal(error.status, expected.status);
const newUser = yield chai_1.assert.isFulfilled(passwordProvider.authenticateOrCreateUser({
username,
password: "new_password",
register: false
}));
chai_1.assert.equal(newUser.userId, user.userId);
}));
});
describe("and token is invalid", () => {
it("throws an error", () => __awaiter(this, void 0, void 0, function* () {
const error = yield chai_1.assert.isRejected(server.authClient.completePasswordReset("invalid-token", "new_password"));
const expected = new errors.realm.AccessDenied();
chai_1.assert.equal(error.status, expected.status);
}));
});
});
});
describe("get access token", () => {
const testCases = [
{ isAdmin: false, isPartial: false, isGlobal: false, existingRealmType: undefined, expected: undefined },
{ isAdmin: false, isPartial: false, isGlobal: false, existingRealmType: index_2.RealmType.full, expected: undefined },
{ isAdmin: false, isPartial: false, isGlobal: false, existingRealmType: index_2.RealmType.reference, expected: new errors.realm.InvalidRealmType(index_2.RealmType.full, index_2.RealmType.reference) },
{ isAdmin: false, isPartial: false, isGlobal: true, existingRealmType: undefined, expected: new errors.realm.AccessDenied() },
{ isAdmin: false, isPartial: false, isGlobal: true, existingRealmType: index_2.RealmType.full, expected: new errors.realm.AccessDenied() },
{ isAdmin: false, isPartial: false, isGlobal: true, existingRealmType: index_2.RealmType.reference, expected: new errors.realm.InvalidRealmType(index_2.RealmType.full, index_2.RealmType.reference) },
{ isAdmin: false, isPartial: true, isGlobal: false, existingRealmType: undefined, expected: undefined },
{ isAdmin: false, isPartial: true, isGlobal: false, existingRealmType: index_2.RealmType.full, expected: new errors.realm.InvalidRealmType(index_2.RealmType.reference, index_2.RealmType.full) },
{ isAdmin: false, isPartial: true, isGlobal: false, existingRealmType: index_2.RealmType.reference, expected: undefined },
{ isAdmin: false, isPartial: true, isGlobal: true, existingRealmType: undefined, expected: new errors.realm.AccessDenied() },
{ isAdmin: false, isPartial: true, isGlobal: true, existingRealmType: index_2.RealmType.full, expected: new errors.realm.InvalidRealmType(index_2.RealmType.reference, index_2.RealmType.full) },
{ isAdmin: false, isPartial: true, isGlobal: true, existingRealmType: index_2.RealmType.reference, expected: undefined },
{ isAdmin: true, isPartial: false, isGlobal: false, existingRealmType: undefined, expected: undefined },
{ isAdmin: true, isPartial: false, isGlobal: false, existingRealmType: index_2.RealmType.full, expected: undefined },
{ isAdmin: true, isPartial: false, isGlobal: false, existingRealmType: index_2.RealmType.reference, expected: undefined },
{ isAdmin: true, isPartial: false, isGlobal: true, existingRealmType: undefined, expected: undefined },
{ isAdmin: true, isPartial: false, isGlobal: true, existingRealmType: index_2.RealmType.full, expected: undefined },
{ isAdmin: true, isPartial: false, isGlobal: true, existingRealmType: index_2.RealmType.reference, expected: undefined },
{ isAdmin: true, isPartial: true, isGlobal: false, existingRealmType: undefined, expected: undefined },
{ isAdmin: true, isPartial: true, isGlobal: false, existingRealmType: index_2.RealmType.full, expected: new errors.realm.InvalidRealmType(index_2.RealmType.reference, index_2.RealmType.full) },
{ isAdmin: true, isPartial: true, isGlobal: false, existingRealmType: index_2.RealmType.reference, expected: undefined },
{ isAdmin: true, isPartial: true, isGlobal: true, existingRealmType: undefined, expected: undefined },
{ isAdmin: true, isPartial: true, isGlobal: true, existingRealmType: index_2.RealmType.full, expected: new errors.realm.InvalidRealmType(index_2.RealmType.reference, index_2.RealmType.full) },
{ isAdmin: true, isPartial: true, isGlobal: true, existingRealmType: index_2.RealmType.reference, expected: undefined }
];
for (const testCase of testCases) {
const description = `when user is ${testCase.isAdmin ? "" : "NOT "}admin ` +
`and path is ${testCase.isPartial ? "" : "NOT "}partial and ${testCase.isGlobal ? "" : "NOT "}global ` +
`and existing Realm is ${testCase.existingRealmType}`;
describe(description, () => {
it(`should ${testCase.expected === undefined ? "succeed" : "throw an error"}`, function () {
return __awaiter(this, void 0, void 0, function* () {
this.timeout(10000000);
const user = yield service.createOrUpdateUser(uuid.v4(), "password", testCase.isAdmin);
let realmPath = `/${uuid.v4()}`;
if (!testCase.isGlobal) {
realmPath = `/${user.userId}${realmPath}`;
}
if (testCase.existingRealmType) {
yield server.realmDirectoryClient.findByPath({
shouldCreate: true,
realmType: testCase.existingRealmType,
realmPath,
ownerId: user.userId
});
}
if (testCase.isPartial) {
realmPath += `/__partial/${user.userId}/${uuid.v4()}`;
}
const refreshToken = new Token_1.RefreshToken({
isAdmin: testCase.isAdmin,
appId: "",
identity: user.userId
});
const promise = service["accessToken"](user, realmPath, refreshToken, refreshToken.sign(server.privateKey));
if (testCase.expected === undefined) {
const result = yield chai_1.assert.isFulfilled(promise);
chai_1.assert.equal(result.access_token.token_data.identity, user.userId);
chai_1.assert.equal(result.access_token.token_data.path, realmPath);
}
else {
const error = yield chai_1.assert.isRejected(promise);
chai_1.assert.equal(error.code, testCase.expected.code);
chai_1.assert.equal(error.status, testCase.expected.status);
}
});
});
});
}
});
});
describe("runtime configuration API", () => {
const defaultProviders = ["Password", "Anonymous", "Nickname"].map(p => `${p}AuthProvider`);
const allProviders = ["Azure", "Cloudkit", "Facebook", "Google", "Jwt"].map(p => `${p}AuthProvider`).concat(defaultProviders);
let server;
let service;
const isEmpty = (obj) => {
if (!obj) {
return true;
}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
return false;
}
}
return true;
};
beforeEach(() => __awaiter(this, void 0, void 0, function* () {
server = new TestServer_1.TestServer();
yield server.start();
service = server.getService("auth");
}));
afterEach(() => __awaiter(this, void 0, void 0, function* () {
yield server.shutdown();
}));
describe("get providers", () => {
describe("when default", () => {
it("should contain password, anonymous, and nickname", () => __awaiter(this, void 0, void 0, function* () {
const providers = yield server.authClient.getProviders();
chai_1.assert.equal(providers.length, 3);
for (const item of defaultProviders) {
const provider = providers.find(p => p.type === item);
chai_1.assert.isDefined(provider);
chai_1.assert.equal(provider.name, provider.type);
if (provider.name === "PasswordAuthProvider") {
chai_1.assert.isDefined(provider.config);
chai_1.assert.isTrue(provider.config.autoCreateAdminUser);
}
else {
chai_1.assert.isTrue(isEmpty(provider.config));
}
}
}));
});
describe("when nickname has been removed", () => {
it("should contain password and anonymous", () => __awaiter(this, void 0, void 0, function* () {
let providers = yield server.authClient.getProviders();
chai_1.assert.equal(providers.length, 3);
yield server.authClient.removeProvider("NicknameAuthProvider");
providers = yield index_1.waitAsync(() => server.authClient.getProviders(), providers => providers.length === 2);
chai_1.assert.equal(providers.length, 2);
const expected = defaultProviders.filter(p => p !== "NicknameAuthProvider");
for (const item of expected) {
const provider = providers.find(p => p.type === item);
chai_1.assert.isDefined(provider);
}
}));
});
describe("when facebook is added", () => {
it("should contain password, anonymous, nickname, facebook", () => __awaiter(this, void 0, void 0, function* () {
let providers = yield server.authClient.getProviders();
chai_1.assert.equal(providers.length, 3);
yield server.authClient.addProvider("FacebookAuthProvider");
providers = yield index_1.waitAsync(() => server.authClient.getProviders(), providers => providers.length === 4);
chai_1.assert.equal(providers.length, 4);
const expected = defaultProviders.concat("FacebookAuthProvider");
for (const item of expected) {
const provider = providers.find(p => p.type === item);
chai_1.assert.isDefined(provider);
}
}));
});
});
describe("add provider", () => {
beforeEach(function () {
return __awaiter(this, void 0, void 0, function* () {
yield Promise.all(defaultProviders.map(p => {
return server.authClient.removeProvider(p);
}));
yield index_1.waitAsync(() => server.authClient.getProviders(), providers => providers.length === 0);
});
});
for (const provider of allProviders) {
it(`should be able to add ${provider}`, () => __awaiter(this, void 0, void 0, function* () {
yield server.authClient.addProvider(provider, {});
const providers = yield index_1.waitAsync(() => server.authClient.getProviders(), providers => providers.length === 1);
chai_1.assert.equal(providers.length, 1);
chai_1.assert.equal(providers[0].name, provider);
chai_1.assert.equal(providers[0].type, provider);
}));
}
const jwtConfig = {
publicKey: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsPJV7FJ5iOgkI6B9o+sE\n/aNHAzfXDNt059CPQathgUvdbpjanfRkNmYhmx7Bfa8ybPFxJZTs53KoLXuAN3uR\nuxFPL6Ki9320JRxlA/zLN7OKklY+M3ftQcvqYuyLvUHwDEG33p5gHngUP7MH+67x\nLWUGSRq2oi6261LwloCceiyzv4UYbxOTvgMC7RYTAcIRfreJTSuww4deffSCtaqd\nbyJLW3G4qUMfEAF5dwxZzx3hSyg9mCyimzdsD5s7TjpQq+mdHudj5mFvN07y11A0\nZqP6KyWQX7JETLJZTJgelqpynqmnNzdGgnxwGkM8h2LlOCXk+z3OpSzXvwfw4AXo\nJwIDAQAB\n-----END PUBLIC KEY-----"
};
const validUserAccessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJmOTVjYzdjZi02MGI3LTQ5YjItYWI0Yi0xZmRjMjgwZWIwYmQiLCJpYXQiOjE1MTEyNzM2NzEsImV4cCI6NDY2NzAzMzY3MX0.AUZiMB-0LyraIWL-CjHGdZR-KdRzqfWPxhyuzsdISgdQdGeXPDd78b_D1mo5c4lBUjJsuTUBJ38Ao5EVnKixe-BbsnasWLNEXErIMD2v3ip4edfqW3oOz1MIdyw-_UEBAlfDqzu3cVtDJlMk1kSbsvxILPxvf0K8mS282qxDR-bSGUhP0rfDZ26A4G3gCOeHes8hZCk5RHIovmIz3u7rmUXogNZ9gC6hwf7YE8HoAZrOIRxQPfPv4UQTpb0i6eOAaA2UL-UDb9PlfrKkw9Z35CmPqDq3DVCWX51mMSNP3edVeDTx2dA7Vv86yRVa1SL5HRcts_l0WHrq4C6qRDTq3Q";
it("should be able to add provider without config", () => __awaiter(this, void 0, void 0, function* () {
yield server.authClient.addProvider("AnonymousAuthProvider");
let providers = yield index_1.waitAsync(() => server.authClient.getProviders(), providers => providers.length === 1);
chai_1.assert.equal(providers.length, 1);
chai_1.assert.isTrue(isEmpty(providers[0].config));
chai_1.assert.equal(providers[0].name, "AnonymousAuthProvider");
chai_1.assert.equal(providers[0].type, "AnonymousAuthProvider");
const user = yield chai_1.assert.isFulfilled(Realm.Sync.User.login(server.url, Realm.Sync.Credentials.anonymous()));
chai_1.assert.isDefined(user);
chai_1.assert.isDefined(user.identity);
chai_1.assert.isFalse(user.isAdmin);
yield server.authClient.removeProvider("AnonymousAuthProvider");
providers = yield index_1.waitAsync(() => server.authClient.getProviders(), providers => providers.length === 0);
chai_1.assert.equal(providers.length, 0);
}));
it("should set its config", () => __awaiter(this, void 0, void 0, function* () {
yield server.authClient.addProvider("JwtAuthProvider", jwtConfig);
const providers = yield index_1.waitAsync(() => server.authClient.getProviders(), providers => providers.length === 1);
chai_1.assert.equal(providers.length, 1);
chai_1.assert.deepEqual(providers[0].config, jwtConfig);
yield Realm.Sync.User.login(server.url, Realm.Sync.Credentials.jwt(validUserAccessToken));
const user = yield chai_1.assert.isFulfilled(Realm.Sync.User.login(server.url, Realm.Sync.Credentials.jwt(validUserAccessToken)));
chai_1.assert.isDefined(user);
chai_1.assert.isDefined(user.identity);
chai_1.assert.isFalse(user.isAdmin);
}));
it("should set its name", () => __awaiter(this, void 0, void 0, function* () {
const providerName = "Foo";
const namedJwtConfig = Object.assign({
providerName
}, jwtConfig);
yield server.authClient.addProvider("JwtAuthProvider", namedJwtConfig, providerName);
let providers = yield index_1.waitAsync(() => server.authClient.getProviders(), providers => providers.length === 1);
chai_1.assert.equal(providers.length, 1);
chai_1.assert.deepEqual(providers[0].config, namedJwtConfig);
chai_1.assert.equal(providers[0].name, providerName);
chai_1.assert.equal(providers[0].type, "JwtAuthProvider");
const user = yield chai_1.assert.isFulfilled(Realm.Sync.User.login(server.url, Realm.Sync.Credentials.jwt(validUserAccessToken, providerName)));
chai_1.assert.isDefined(user);
chai_1.assert.isDefined(user.identity);
chai_1.assert.isFalse(user.isAdmin);
yield se