authelia/server/test/storage/UserDataStore.test.ts
Clement Michaud 3a88ca95b8 Check TOTP token with window of 1
A window of 1 means the token is checked against current time slot T
as well as at time slot T-1 and T+1.
A time slot is 30 seconds by default in Authelia.
2017-10-15 00:44:10 +02:00

265 lines
8.3 KiB
TypeScript

import * as Assert from "assert";
import * as Sinon from "sinon";
import * as MockDate from "mockdate";
import BluebirdPromise = require("bluebird");
import { UserDataStore } from "../../src/lib/storage/UserDataStore";
import { TOTPSecret } from "../../types/TOTPSecret";
import { U2FRegistration } from "../../types/U2FRegistration";
import { AuthenticationTraceDocument } from "../../src/lib/storage/AuthenticationTraceDocument";
import { CollectionStub } from "../mocks/storage/CollectionStub";
import { CollectionFactoryStub } from "../mocks/storage/CollectionFactoryStub";
describe("test user data store", function () {
let factory: CollectionFactoryStub;
let collection: CollectionStub;
let userId: string;
let appId: string;
let totpSecret: TOTPSecret;
let u2fRegistration: U2FRegistration;
beforeEach(function () {
factory = new CollectionFactoryStub();
collection = new CollectionStub();
userId = "user";
appId = "https://myappId";
totpSecret = {
ascii: "abc",
base32: "ABCDKZLEFZGREJK",
otpauth_url: "totp://test",
google_auth_qr: "dummy",
hex: "dummy",
qr_code_ascii: "dummy",
qr_code_base32: "dummy",
qr_code_hex: "dummy"
};
u2fRegistration = {
keyHandle: "KEY_HANDLE",
publicKey: "publickey"
};
});
it("should correctly creates collections", function () {
new UserDataStore(factory);
Assert.equal(4, factory.buildStub.callCount);
Assert(factory.buildStub.calledWith("authentication_traces"));
Assert(factory.buildStub.calledWith("identity_validation_tokens"));
Assert(factory.buildStub.calledWith("u2f_registrations"));
Assert(factory.buildStub.calledWith("totp_secrets"));
});
describe("TOTP secrets collection", function () {
it("should save a totp secret", function () {
factory.buildStub.returns(collection);
collection.updateStub.returns(BluebirdPromise.resolve());
const dataStore = new UserDataStore(factory);
return dataStore.saveTOTPSecret(userId, totpSecret)
.then(function (doc) {
Assert(collection.updateStub.calledOnce);
Assert(collection.updateStub.calledWith({ userId: userId }, {
userId: userId,
secret: totpSecret
}, { upsert: true }));
return BluebirdPromise.resolve();
});
});
it("should retrieve a totp secret", function () {
factory.buildStub.returns(collection);
collection.findOneStub.withArgs().returns(BluebirdPromise.resolve());
const dataStore = new UserDataStore(factory);
return dataStore.retrieveTOTPSecret(userId)
.then(function (doc) {
Assert(collection.findOneStub.calledOnce);
Assert(collection.findOneStub.calledWith({ userId: userId }));
return BluebirdPromise.resolve();
});
});
});
describe("U2F secrets collection", function () {
it("should save a U2F secret", function () {
factory.buildStub.returns(collection);
collection.updateStub.returns(BluebirdPromise.resolve());
const dataStore = new UserDataStore(factory);
return dataStore.saveU2FRegistration(userId, appId, u2fRegistration)
.then(function (doc) {
Assert(collection.updateStub.calledOnce);
Assert(collection.updateStub.calledWith({
userId: userId,
appId: appId
}, {
userId: userId,
appId: appId,
registration: u2fRegistration
}, { upsert: true }));
return BluebirdPromise.resolve();
});
});
it("should retrieve a U2F secret", function () {
factory.buildStub.returns(collection);
collection.findOneStub.withArgs().returns(BluebirdPromise.resolve());
const dataStore = new UserDataStore(factory);
return dataStore.retrieveU2FRegistration(userId, appId)
.then(function (doc) {
Assert(collection.findOneStub.calledOnce);
Assert(collection.findOneStub.calledWith({
userId: userId,
appId: appId
}));
return BluebirdPromise.resolve();
});
});
});
describe("Regulator traces collection", function () {
it("should save a trace", function () {
factory.buildStub.returns(collection);
collection.insertStub.returns(BluebirdPromise.resolve());
const dataStore = new UserDataStore(factory);
return dataStore.saveAuthenticationTrace(userId, true)
.then(function (doc) {
Assert(collection.insertStub.calledOnce);
Assert(collection.insertStub.calledWith({
userId: userId,
date: Sinon.match.date,
isAuthenticationSuccessful: true
}));
return BluebirdPromise.resolve();
});
});
function should_retrieve_latest_authentication_traces(count: number) {
factory.buildStub.returns(collection);
collection.findStub.withArgs().returns(BluebirdPromise.resolve());
const dataStore = new UserDataStore(factory);
return dataStore.retrieveLatestAuthenticationTraces(userId, count)
.then(function (doc: AuthenticationTraceDocument[]) {
Assert(collection.findStub.calledOnce);
Assert(collection.findStub.calledWith({
userId: userId,
}, { date: -1 }, count));
return BluebirdPromise.resolve();
});
}
it("should retrieve 3 latest failed authentication traces", function () {
should_retrieve_latest_authentication_traces(3);
});
});
describe("Identity validation collection", function () {
it("should save a identity validation token", function () {
factory.buildStub.returns(collection);
collection.insertStub.returns(BluebirdPromise.resolve());
const dataStore = new UserDataStore(factory);
const maxAge = 400;
const token = "TOKEN";
const challenge = "CHALLENGE";
return dataStore.produceIdentityValidationToken(userId, token, challenge, maxAge)
.then(function (doc) {
Assert(collection.insertStub.calledOnce);
Assert(collection.insertStub.calledWith({
userId: userId,
token: token,
challenge: challenge,
maxDate: Sinon.match.date
}));
return BluebirdPromise.resolve();
});
});
it("should consume an identity token successfully", function () {
factory.buildStub.returns(collection);
MockDate.set(100);
const token = "TOKEN";
const challenge = "CHALLENGE";
collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({
userId: "USER",
token: token,
challenge: challenge,
maxDate: new Date()
}));
collection.removeStub.returns(BluebirdPromise.resolve());
const dataStore = new UserDataStore(factory);
MockDate.set(80);
return dataStore.consumeIdentityValidationToken(token, challenge)
.then(function (doc) {
MockDate.reset();
Assert(collection.findOneStub.calledOnce);
Assert(collection.findOneStub.calledWith({
token: token,
challenge: challenge
}));
Assert(collection.removeStub.calledOnce);
Assert(collection.removeStub.calledWith({
token: token,
challenge: challenge
}));
return BluebirdPromise.resolve();
});
});
it("should consume an expired identity token", function () {
factory.buildStub.returns(collection);
MockDate.set(0);
const token = "TOKEN";
const challenge = "CHALLENGE";
collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({
userId: "USER",
token: token,
challenge: challenge,
maxDate: new Date()
}));
const dataStore = new UserDataStore(factory);
MockDate.set(80000);
return dataStore.consumeIdentityValidationToken(token, challenge)
.then(function () { return BluebirdPromise.reject(new Error("should not be here")); })
.catch(function () {
MockDate.reset();
Assert(collection.findOneStub.calledOnce);
Assert(collection.findOneStub.calledWith({
token: token,
challenge: challenge
}));
return BluebirdPromise.resolve();
});
});
});
});