baqend
Version:
Baqend JavaScript SDK
796 lines (688 loc) • 26.4 kB
JavaScript
if (typeof module !== 'undefined') {
require('./node');
}
/* global OTPAuth */
describe('Test user and roles', function () {
var emf, db;
var RENEW_TIMEOUT = 2000;
this.timeout(RENEW_TIMEOUT * 5);
before(async function () {
emf = new DB.EntityManagerFactory({
host: env.TEST_SERVER,
tokenStorage: await helper.rootTokenStorage,
});
return emf.createEntityManager()
.ready()
.then(function () {
var userEntity = emf.metamodel.entity('User');
if (!userEntity.getAttribute('email')) {
userEntity.addAttribute(new DB.metamodel.SingularAttribute('email', emf.metamodel.baseType(String)));
return emf.metamodel.save();
}
});
});
beforeEach(function () {
db = emf.createEntityManager();
return db.ready();
});
describe('user factory', function () {
it('should have methods', function () {
expect(db.User).be.ok;
expect(db.User.register).be.ok;
expect(db.User.login).be.ok;
expect(db.User.logout).be.ok;
expect(db.User.newPassword).be.ok;
});
it('should not share the tokenStorage with the emf', function () {
expect(db.tokenStorage).not.equal(emf.tokenStorage);
});
it('should share the tokenStorage with the emf if createEm is true', function () {
var db = emf.createEntityManager(true);
expect(db.tokenStorage).equal(emf.tokenStorage);
});
it('should register and login a new user', function () {
var login = helper.makeLogin();
return db.User.register(login, 'secret').then(function (user) {
expect(user).be.ok;
expect(user instanceof DB.binding.User).be.true;
expect(user.id).be.ok;
expect(user.version).be.ok;
expect(user._metadata.isPersistent).be.true;
expect(user._metadata.isDirty).be.false;
expect(user.username).equals(login);
expect(user.password).be.undefined;
expect(user).equals(db.User.me);
expect(db.token).be.ok;
});
});
it('should not set token and me when loginOption is NO_LOGIN', function () {
var user = new db.User({ username: helper.makeLogin(), email: 'test@mail.de' });
return db.User.register(user, 'secret', db.User.LoginOption.NO_LOGIN).then(function () {
expect(db.me).not.ok;
expect(db.token).not.ok;
});
});
it('should register user from object', function () {
var user = new db.User({ username: helper.makeLogin(), email: 'test@mail.de' });
return db.User.register(user, 'secret').then(function (loaded) {
expect(loaded.username).eqls(user.username);
expect(loaded.email).eqls('test@mail.de');
});
});
it('should fail to register if username is missing', async function () {
var user = { foobar: helper.makeLogin() };
try {
await db.User.register(user, 'secret');
expect.fail();
} catch {
}
});
it('should logout an user', function () {
var login = helper.makeLogin();
return db.User.register(login, 'secret').then(function ( ) {
return db.User.logout();
}).then(function () {
expect(db.User.me).be.null;
expect(db.token).be.null;
});
});
it('should not register a user twice', function () {
var login = helper.makeLogin();
var promise = db.User.register(login, 'secret').then(function (user) {
expect(user.username).be.equals(login);
});
expect(function () {
db.User.register(login, 'secret');
}).throw(Error);
return promise;
});
it('should not register an existing user', async function () {
var login = helper.makeLogin();
await db.User.register(login, 'secret');
await db.User.logout();
try {
await db.User.register(login, 'secret');
expect.fail();
} catch {}
expect(db.token).be.null;
expect(db.User.me).be.null;
});
it('should login with valid credentials', function () {
var login = helper.makeLogin();
var user;
return db.User.register(login, 'secret').then(function (u) {
user = u;
expect(user).be.ok;
expect(user instanceof DB.binding.User).be.true;
expect(user.id).be.ok;
expect(user.version).be.ok;
expect(user._metadata.isPersistent).be.true;
expect(user._metadata.isDirty).be.false;
expect(user.username).equals(login);
expect(user.password).be.undefined;
expect(user).equals(db.User.me);
expect(db.token).be.ok;
return db.User.logout();
}).then(function () {
return db.User.login(login, 'secret');
}).then(function (u) {
expect(user).equals(u);
});
});
it('should not login an unknown user', async function () {
var login = helper.makeLogin();
try {
await db.User.login(login, 'secret');
expect.fail();
} catch { }
});
it('should not login with invalid credentials', async function () {
var login = helper.makeLogin();
await db.User.register(login, 'secret');
await db.User.logout();
try {
await db.User.login(login, 'hackit');
expect.fail();
} catch {}
});
it('should not login twice', function () {
var login = helper.makeLogin();
return db.User.register(login, 'secret').then(function () {
return expect(function () {
db.User.login(login, 'secret');
}).throw(Error);
}).then(function () {
return db.User.logout();
}).then(function () {
return db.User.login(login, 'secret');
})
.then(function () {
return expect(function () {
return db.User.login(login, 'secret');
}).to.throw(Error);
});
});
it('should logout user', function () {
var login = helper.makeLogin();
return db.User.register(login, 'secret').then(function () {
expect(db.token).be.ok;
expect(db.User.me).be.ok;
return db.logout();
}).then(function () {
expect(db.token).be.null;
expect(db.User.me).be.null;
});
});
it('should renew user token', function () {
var login = helper.makeLogin();
var oldToken;
return db.User.register(login, 'secret').then(function () {
return helper.sleep(RENEW_TIMEOUT);
}).then(function () {
expect(db.token).be.ok;
oldToken = db.token;
return db.renew();
}).then(function () {
expect(db.token).not.eqls(oldToken);
});
});
it('should not old cached user tokens from a renewal', function () {
var login = helper.makeLogin();
var oldToken;
return db.User.register(login, 'secret').then(function () {
return helper.sleep(RENEW_TIMEOUT);
}).then(function () {
expect(db.token).be.ok;
oldToken = db.token;
// create a browser cached object, with a renewed token
return db.renew();
}).then(function () {
// renew by side effect
var newToken = db.token;
expect(db.token).not.eqls(oldToken);
db.token = oldToken;
expect(db.token).eqls(newToken);
});
});
it('should change password', async function () {
var oldLogin = helper.makeLogin();
var oldToken;
await db.User.register(oldLogin, 'secret');
oldToken = db.token;
await helper.sleep(RENEW_TIMEOUT);
await db.me.newPassword('secret', 'newSecret');
expect(oldToken).not.eqls(db.token);
await db.User.logout();
await db.User.login(oldLogin, 'newSecret');
await db.User.logout();
try {
await db.User.login(oldLogin, 'secret');
expect.fail();
} catch {}
});
it('should create user with MFA disabled', async function() {
var login = helper.makeLogin()
await db.User.register(login, 'secret')
var response = await db.getMFAStatus()
expect(response).be.equal('DISABLED')
})
it('should init MFA', async function () {
var login = helper.makeLogin()
await db.User.register(login, 'secret');
const response1 = await db.initMFA();
const { keyUri} = response1;
const totp = OTPAuth.URI.parse(keyUri);
const code = totp.generate();
await db.finishMFA(code);
const mfaStatus = await db.getMFAStatus();
expect(mfaStatus).to.equal('REQUIRED');
})
it('should not init MFA with false code', async function () {
const login = helper.makeLogin();
await db.User.register(login, 'secret');
const responseInitMFA = await db.initMFA();
const { keyUri } = responseInitMFA;
const code = '123456';
try {
await db.finishMFA(code);
expect.fail('Invalid code was accepted');
} catch (response) {
expect(response.status).to.equal(400);
expect(response.message).to.equal('MFA code invalid');
}
})
it('should disable MFA', async function () {
const login = helper.makeLogin();
await db.User.register(login, 'secret');
let response = await db.initMFA();
const { keyUri } = response;
const totp = OTPAuth.URI.parse(keyUri);
const code = totp.generate();
await db.finishMFA(code);
let mfaStatus = await db.getMFAStatus();
expect(mfaStatus).to.equal('REQUIRED');
await db.disableMFA();
mfaStatus = await db.getMFAStatus();
expect(mfaStatus).to.equal('DISABLED');
})
it('should require MFA code during login', async function () {
var login = helper.makeLogin()
let user = await db.User.register(login, 'secret')
const response1 = await db.initMFA()
const { keyUri } = response1
// Create second factor through usage of the keyUri and use it to create the code
// to confirm mfa activation
let totp = OTPAuth.URI.parse(keyUri)
const code = totp.generate()
user = await db.finishMFA(code)
expect(user.username).eq(login)
const mfaStatus = await db.getMFAStatus()
expect(mfaStatus).to.equal('REQUIRED')
await db.User.logout()
expect(db.User.me).to.equal(null)
try {
await db.User.login(login, 'secret')
// if the login proceeds without mfa, throw error
expect.fail('login proceeded without mfa')
} catch (mfaError) {
const { message, token } = mfaError
expect(message).to.equal('MFA Required')
const code = totp.generate()
const codeResponse = await db.submitMFACode(code, token)
expect(codeResponse).equals(user)
expect(codeResponse.username).eq(login)
}
})
it("should prevent mfa login without a valid code", async function () {
var login = helper.makeLogin();
await db.User.register(login, "secret");
const response = await db.initMFA();
const { keyUri } = response;
const totp = OTPAuth.URI.parse(keyUri);
const code = totp.generate();
const user = await db.finishMFA(code);
const mfaStatus = await db.getMFAStatus();
expect(mfaStatus).to.equal("REQUIRED");
await db.User.logout();
expect(db.User.me).to.equal(null);
try {
await db.User.login(login, "secret");
expect.fail("login proceeded without mfa");
} catch (mfaError) {
const { message, token } = mfaError;
expect(message).to.equal("MFA Required");
const code = '123456';
try {
await db.submitMFACode(code, token);
expect.fail("login proceeded without valid code");
} catch (response) {
expect(response.status).to.equal(400);
expect(response.message).to.equal('MFA code invalid');
}
}
});
it('should keep user login when newPassword is called with invalid credentials', async function () {
var oldLogin = helper.makeLogin();
var oldToken;
await db.User.register(oldLogin, 'secret');
oldToken = db.token;
await helper.sleep(RENEW_TIMEOUT);
try {
await db.me.newPassword('wrong-secret', 'newSecret');
expect.fail();
} catch (e) {
expect(e.message).to.contain('User name or password is incorrect');
}
expect(oldToken).eqls(db.token);
expect(db.me.username).eqls(oldLogin);
});
it('should be allowed to change password as root', async function () {
var oldLogin = helper.makeLogin();
var oldToken;
await db.User.register(oldLogin, 'secret');
await db.User.logout();
await db.User.login('root', 'root');
oldToken = db.token;
expect(db.me.username).eqls('root');
await db.User.newPassword(oldLogin, '', 'newSecret');
expect(db.me.username).eqls('root');
expect(db.token).eqls(oldToken);
await db.User.logout();
await expect(await db.User.login(oldLogin, 'newSecret')).be.ok;
await db.User.logout();
try {
await db.User.login(oldLogin, 'secret');
expect.fail();
} catch {}
});
it('should not be allowed to insert user', async function () {
var name = helper.makeLogin();
var newUser = db.User.fromJSON({
username: name,
});
try {
await newUser.save();
expect.fail();
} catch {}
});
it('should not be allowed to register with an empty password', async function () {
try {
await db.User.register(helper.makeLogin(), '');
expect.fail();
} catch {}
});
it('should fail change username with email verification disabled', async function () {
var login = helper.makeLogin().concat('@baqend.com');
var newLogin = helper.makeLogin().concat('@baqend.com');
await db.User.register(login, 'secret');
await db.User.logout();
await db.User.login('root', 'root');
expect(db.me.username).eqls('root');
try {
await db.User.changeUsername(login, newLogin, 'secret');
expect.fail();
} catch (e) {
expect(e.message).to.contain('Email verification not enabled');
}
});
it('should create api token for root', async function () {
await db.User.login('root', 'root');
let apiToken = await db.User.me.requestAPIToken();
expect(apiToken).not.be.null;
apiToken = await db.User.requestAPIToken(db.User.me);
expect(apiToken).not.be.null;
apiToken = await db.User.requestAPIToken('1');
expect(apiToken).not.be.null;
});
it('should create api token for other user', function () {
var user = helper.makeLogin();
var regUser;
return db.User.register(user, 'secret', db.User.LoginOption.NO_LOGIN).then(function (usr) {
regUser = usr;
return db.User.login('root', 'root');
}).then(function () {
return db.User.requestAPIToken(regUser);
}).then(function (apiToken) {
expect(apiToken).not.be.null;
return db.User.requestAPIToken(regUser.id);
})
.then(function (apiToken) {
expect(apiToken).not.be.null;
});
});
it('should only be allowed for admins to create API token', async function () {
var user = helper.makeLogin();
await db.User.register(user, 'secret');
try {
await db.User.me.requestAPIToken();
expect.fail();
} catch {}
});
it('should only be allowed for admins to revoke tokens', async function () {
var user = helper.makeLogin();
await db.User.register(user, 'secret');
try {
await db.User.revokeAllTokens(db.User.me);
expect.fail();
} catch {}
});
it('should return a new token if revoking own tokens', function () {
var token;
return db.User.login('root', 'root').then(function () {
return helper.sleep(1000);
}).then(function () {
token = db.token;
return db.User.revokeAllTokens(db.User.me);
}).then(function () {
expect(token).not.equal(db.token);
});
});
});
describe('on global DB', function () {
before(async function () {
await helper.ensureGlobalConnected();
await DB.User.logout();
});
afterEach(function () {
return DB.User.logout();
});
it('should remove token if password has been changed', function () {
var login = helper.makeLogin();
return DB.User.register(login, 'secret').then(function () {
return helper.sleep(RENEW_TIMEOUT);
}).then(function () {
return db.User.login(login, 'secret');
}).then(function () {
return db.User.me.newPassword('secret', 'newSecret');
})
.then(function () {
expect(DB.tokenStorage.token).be.ok;
return DB.renew();
})
.then(function () {
expect(DB.tokenStorage.token).be.null;
expect(DB.User.me).be.null;
expect(DB.token).be.null;
});
});
it('should fail change username with email verification disabled', async function () {
var login = helper.makeLogin().concat('@baqend.com');
var newLogin = helper.makeLogin().concat('@baqend.com');
await db.User.register(login, 'secret');
await db.User.logout();
await db.User.login('root', 'root');
expect(db.me.username).eqls('root');
try {
await db.User.me.changeUsername(newLogin, 'secret');
expect.fail();
} catch (e) {
expect(e.message).to.contain('Email verification not enabled');
}
});
it('should remove token if token is invalid', function () {
var login = helper.makeLogin();
return DB.User.register(login, 'secret').then(function () {
var { token } = DB.tokenStorage;
expect(token).be.ok;
DB.tokenStorage.update(token.substring(0, token.length - 1) + (token.substr(token.length - 1, token.length) === '0' ? '1' : '0'));
return DB.renew();
}).then(function (user) {
expect(user).be.null;
expect(DB.tokenStorage.token).be.null;
expect(DB.User.me).be.null;
expect(DB.token).be.null;
});
});
it('should not remove token if not global', function () {
var login = helper.makeLogin();
var oldToken;
return DB.User.register(login, 'secret').then(function () {
return db.User.login(login, 'secret');
}).then(function () {
expect(DB.token).be.ok;
oldToken = DB.token;
db.token = db.token.replace(/.{1}$/, db.token.substr(db.token.length - 1, db.token.length) === '0' ? '1' : '0');
return db.renew();
}).then(function (user) {
expect(user).be.null;
expect(DB.token).eqls(oldToken);
});
});
it('should use global storage if tokenStorage is true', function () {
var login = helper.makeLogin();
return DB.User.register(login, 'secret').then(function () {
expect(DB.tokenStorage).eqls(DB.entityManagerFactory.tokenStorage);
expect(DB.me).be.ok;
expect(DB.token).be.ok;
return DB.renew();
});
});
it('should remove token by logout if tokenStorage is true', function () {
var login = helper.makeLogin();
return DB.User.register(login, 'secret').then(function () {
expect(DB.tokenStorage).eqls(DB.entityManagerFactory.tokenStorage);
expect(DB.me).be.ok;
expect(DB.token).be.ok;
return DB.logout();
}).then(async function () {
expect(await DB.renew()).be.null;
});
});
it('should autologin on new EntityManager instance', function () {
var login = helper.makeLogin();
return DB.User.register(login, 'secret').then(function () {
var userId = DB.User.me.id;
expect(userId).be.ok;
expect(DB.entityManagerFactory.connectData.user).be.ok;
var db = DB.entityManagerFactory.createEntityManager(true);
return db.ready().then(function () {
expect(db.me).be.ok;
expect(db.me.id).eq(userId);
expect(db.token).be.ok;
});
});
});
it('should not login if sharedTokenStorage is logged out', function () {
var login = helper.makeLogin();
return DB.User.register(login, 'secret').then(function () {
expect(DB.entityManagerFactory.connectData.user).be.ok;
return DB.User.logout();
}).then(function () {
expect(DB.entityManagerFactory.connectData.user).be.not.ok;
var localDB = DB.entityManagerFactory.createEntityManager(true);
return localDB.ready().then(function () {
expect(localDB.me).be.not.ok;
expect(localDB.token).be.not.ok;
});
});
});
it('should autologin on when tokenStorage is true', function () {
var login = helper.makeLogin();
return DB.User.register(login, 'secret').then(function () {
var localDB = new DB.EntityManagerFactory(env.TEST_SERVER).createEntityManager(true);
return localDB.ready().then(function () {
expect(localDB.me).be.ok;
expect(localDB.token).be.ok;
});
});
});
it('should not autologin when tokenStorage is false', function () {
var login = helper.makeLogin();
return DB.User.register(login, 'secret').then(function () {
var db = new DB.EntityManagerFactory(env.TEST_SERVER).createEntityManager();
return db.ready().then(function () {
expect(db.me).be.not.ok;
expect(db.token).be.not.ok;
});
});
});
if (typeof localStorage !== 'undefined') {
it('should save token in session storage when register loginOption is false', function () {
var user = new DB.User({ username: helper.makeLogin() });
return DB.User.register(user, 'secret', false).then(function (u) {
expect(u.username).eqls(user.username);
expect(localStorage.getItem(`BAT:${db.connection.origin}`)).be.not.ok;
expect(sessionStorage.getItem(`BAT:${db.connection.origin}`)).be.ok;
});
});
it('should save token in local storage when register loginOption is true', function () {
var user = new DB.User({ username: helper.makeLogin() });
return DB.User.register(user, 'secret', true).then(function (u) {
expect(u.username).eqls(user.username);
expect(localStorage.getItem(`BAT:${db.connection.origin}`)).be.ok;
expect(sessionStorage.getItem(`BAT:${db.connection.origin}`)).be.not.ok;
});
});
it('should save token in session storage when login loginOption is false', function () {
var username = helper.makeLogin();
var user = new DB.User({ username: username });
return DB.User.register(user, 'secret', db.User.LoginOption.NO_LOGIN).then(function () {
return DB.User.login(username, 'secret', false);
}).then(function (u) {
expect(u.username).eqls(user.username);
expect(localStorage.getItem(`BAT:${db.connection.origin}`)).be.not.ok;
expect(sessionStorage.getItem(`BAT:${db.connection.origin}`)).be.ok;
});
});
it('should save token in local storage when login loginOption is true', function () {
var username = helper.makeLogin();
var user = new DB.User({ username: username });
return DB.User.register(user, 'secret', db.User.LoginOption.NO_LOGIN).then(function () {
return DB.User.login(username, 'secret', true);
}).then(function (u) {
expect(u.username).eqls(user.username);
expect(localStorage.getItem(`BAT:${db.connection.origin}`)).be.ok;
expect(sessionStorage.getItem(`BAT:${db.connection.origin}`)).be.not.ok;
});
});
it('should remove token after logout', function () {
var username = helper.makeLogin();
var user = new DB.User({ username: username });
return DB.User.register(user, 'secret').then(function () {
expect(DB.User.me).be.ok;
expect(localStorage.getItem(`BAT:${db.connection.origin}`)).be.ok;
return DB.User.logout();
}).then(function () {
expect(DB.User.me).be.null;
expect(DB.token).be.null;
expect(localStorage.getItem(`BAT:${db.connection.origin}`)).be.not.ok;
expect(sessionStorage.getItem(`BAT:${db.connection.origin}`)).be.not.ok;
});
});
}
});
describe('roles', function () {
var user1, user2, user3;
beforeEach(function () {
user1 = new db.User();
user1.username = helper.makeLogin();
user2 = new db.User();
user2.username = helper.makeLogin();
user3 = new db.User();
user3.username = helper.makeLogin();
return db.User.register(user1, user1.username, db.User.LoginOption.NO_LOGIN).then(function (usr) {
user1 = usr;
return db.User.register(user2, user2.username, db.User.LoginOption.NO_LOGIN);
}).then(function (usr) {
user2 = usr;
return db.User.register(user3, user3.username, db.User.LoginOption.NO_LOGIN);
}).then(function (usr) {
user3 = usr;
});
});
it('should save and load', function () {
var role = new db.Role();
role.addUser(user1);
role.addUser(user3);
expect(role.hasUser(user1)).be.true;
expect(role.hasUser(user2)).be.false;
expect(role.hasUser(user3)).be.true;
return role.insert().then(function () {
expect(role.hasUser(user1)).be.true;
expect(role.hasUser(user2)).be.false;
expect(role.hasUser(user3)).be.true;
role.removeUser(user1);
role.addUser(user2);
return role.save();
}).then(function () {
expect(role.hasUser(user1)).be.false;
expect(role.hasUser(user2)).be.true;
expect(role.hasUser(user3)).be.true;
});
});
it('should renew token', function () {
var login = helper.makeLogin();
var oldToken;
return db.User.register(login, 'secret').then(function () {
return helper.sleep(RENEW_TIMEOUT);
}).then(function () {
oldToken = db.token;
var role = new db.Role();
role.addUser(user1);
return role.insert();
}).then(function () {
expect(oldToken).not.eqls(db.token);
});
});
});
});