Skip to content

Commit

Permalink
Release 0.1.1 (#2)
Browse files Browse the repository at this point in the history
* Print proper error when disallowed

* Add server side tests
  • Loading branch information
Tomasz Przytuła committed Jan 27, 2018
1 parent 2dfc57d commit e8037c9
Show file tree
Hide file tree
Showing 15 changed files with 396 additions and 26 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
.idea
.idea
.npm
.vscode
node_modules
6 changes: 6 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.idea
.npm
.git
.vscode
node_modules
test
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@

## [0.1.0] - 19.01.2018
* Initial release

## [0.1.1] - 27.01.2018
* Print correct error in case of already dissallowed attempt
* Add server side tests
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ Wrapper for a Meteor.loginWithPassword with an addition of rememberMe as a last

The default for rememberMe is true to match the behaviour of Meteor.

## Testing

## TODO:
- support multi server accounts
- tests
You can test this dependency by running `npm run test`
2 changes: 1 addition & 1 deletion client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Accounts } from 'meteor/accounts-base';
import overrideAccountsLogin from './overrideLogin';

const RememberMe = {};

const updateRememberMe = 'tprzytula:rememberMe-update';

RememberMe.loginWithPassword = (user, password, callback = () => {}, rememberMe = true) => {
let flag = rememberMe;
let callbackMethod = () => {};
Expand Down
14 changes: 13 additions & 1 deletion package.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package.describe({
name: 'tprzytula:remember-me',
version: '0.1.0',
version: '0.1.1',
summary: 'Extension for Meteor account-base package with the implementation of rememberMe',
git: 'https://github.com/tprzytulacc/Meteor-RememberMe',
documentation: 'README.md'
Expand All @@ -10,10 +10,22 @@ Package.onUse((api) => {
api.versionsFrom('1.5.2.2');
api.use('ecmascript');
api.use('accounts-base');
api.use('accounts-password');
api.mainModule('client/index.js', 'client');
api.mainModule('server/index.js', 'server');
});

Package.onTest((api) => {
api.use('ecmascript');
api.use('accounts-base');
api.use('accounts-password');
api.use('coffeescript');
api.use('meteortesting:mocha');
api.use('tprzytula:remember-me');
api.use('practicalmeteor:chai');
api.mainModule('test/server/index.js', 'server');
});

Npm.depends({
'crypto-js': '3.1.9-1',
lodash: '4.17.4'
Expand Down
37 changes: 20 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
{
"name": "tprzytula:remember-me",
"version": "0.1.0",
"description": "Extension for Meteor account-base package with the implementation of rememberMe",
"license": "MIT",
"author": "Tomasz Przytuła <[email protected]>",
"repository": {
"type": "git",
"url": "https://github.com/tprzytulacc/Meteor-Remember-Me"
},
"keywords": [
"meteor",
"rememberme",
"autologin",
"login"
],
"homepage": "https://github.com/tprzytulacc/Meteor-Remember-Me"
}
"name": "rememberMe",
"version": "0.1.1",
"description": "Extension for Meteor account-base package with the implementation of rememberMe",
"license": "MIT",
"author": "Tomasz Przytuła <[email protected]>",
"repository": {
"type": "git",
"url": "https://github.com/tprzytulacc/Meteor-Remember-Me"
},
"keywords": [
"meteor",
"rememberme",
"autologin",
"login"
],
"homepage": "https://github.com/tprzytulacc/Meteor-Remember-Me",
"scripts": {
"test": "meteor test-packages ./ --driver-package meteortesting:mocha"
}
}
3 changes: 2 additions & 1 deletion server/authenticator.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ class Authenticator {
return {
result: false,
resultCode: -1,
reason: 'Attempt disallowed by Meteor'
reason: 'Attempt disallowed by Meteor',
error: this.loginAttempt.error
};
}

Expand Down
9 changes: 7 additions & 2 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ export const activate = () => {
methods();
Accounts.validateLoginAttempt((attempt) => {
const authenticator = new Authenticator(attempt);
const { result, resultCode, reason } = authenticator.validateAttempt();
if (!result) {
const { result, resultCode, reason, error } = authenticator.validateAttempt();
if (error) {
throw error;
} else if (!result) {
throw new Meteor.Error(resultCode, reason);
}
return true;
Expand All @@ -32,6 +34,9 @@ export const activate = () => {
export const _updateState = (connectionId, flag) => {
const userLoginToken = Accounts._getLoginToken(connectionId);
const loginTokens = RememberMeHelpers.getAllUserTokens(userLoginToken);
if (!loginTokens) {
return false;
}

const updatedLoginTokens = loginTokens.map((loginToken) => {
const record = loginToken;
Expand Down
12 changes: 12 additions & 0 deletions test/server/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const login = require('./tests/login');
const method = require('./tests/method');
const resume = require('./tests/resume');

/**
* Server-side test cases.
*/
describe('server', () => {
login();
method();
resume();
});
50 changes: 50 additions & 0 deletions test/server/tests/login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const { chai } = require('meteor/practicalmeteor:chai');
const Authenticator = require('../../../server/authenticator').default;
const LoginAttemptGenerator = require('../utils/loginAttemptGenerator');

const expect = chai.expect;
const type = 'password';

module.exports = () => {
/**
* Those tests are covering a basic user login attempt.
* The attempt is invoked if the user is logging for the first
* time by using methods such as Meteor.loginWithPassword.
*/
describe('login attempt', () => {
/**
* The attempt can be disallowed already from the previously
* ran validators. It can be a validator directly from the Meteor core
* saying that the password is wrong but also another one created by the developer.
*
* In this case there is no need to validate the attempt anymore.
* It should be instantly disallowed again.
*/
it('should not pass if the attempt is already disallowed', () => {
const loginAttemptGenerator = new LoginAttemptGenerator({ type });
let loginAttempt = loginAttemptGenerator.getLoginAttempt();
loginAttempt.allowed = false;
const authenticator = new Authenticator(loginAttempt);
const { result, resultCode, reason } = authenticator.validateAttempt();
expect(result).to.be.equal(false);
expect(resultCode).to.be.equal(-1);
expect(reason).to.be.equal('Attempt disallowed by Meteor');
});

/**
* The dependency logic should not affect normal login attempts.
* Because of that if the previously ran validations succeeded the
* dependency should also let it pass further.
*/
it('should pass if the attempt is allowed', () => {
const loginAttemptGenerator = new LoginAttemptGenerator({ type });
let loginAttempt = loginAttemptGenerator.getLoginAttempt();
loginAttempt.allowed = true;
const authenticator = new Authenticator(loginAttempt);
const { result, resultCode, reason } = authenticator.validateAttempt();
expect(result).to.be.equal(true);
expect(resultCode).to.be.equal(0);
expect(reason).to.be.equal('Validation passed');
});
});
};
39 changes: 39 additions & 0 deletions test/server/tests/method.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const RememberMe = require('meteor/tprzytula:remember-me');
const { chai } = require('meteor/practicalmeteor:chai');

const rememberMeMethod = 'tprzytula:rememberMe-update';
const getMeteorMethods = () => Meteor.default_server.method_handlers;
const checkIfMeteorMethodExists = name => name in getMeteorMethods();

module.exports = () => {
/**
* The dependency is significantly affecting how the login system works.
* I'm against the idea of dependencies which are running automatically
* without the developer knowledge. There are situations where developers
* are leaving not used dependencies in the list. In this case it can be
* hard for them to debug and find the reason for their login system
* to work differently than the normal one should.
*/
describe('remember-me method', () => {
/**
* Having this dependency installed should not invoke it.
* The main method used for the communication client <> server
* should not exist.
*/
it('should not exist by default', () => {
const doesExist = checkIfMeteorMethodExists(rememberMeMethod);
expect(doesExist).to.be.equal(false);
});

/**
* After activating the functionality a new meteor method
* should be created. From now all users are being able to
* invoke the 'tprzytula:rememberMe-update' method.
*/
it('should exist after activating the functionality', () => {
RememberMe.activate();
const doesExist = checkIfMeteorMethodExists(rememberMeMethod);
expect(doesExist).to.be.equal(true);
});
});
};
105 changes: 105 additions & 0 deletions test/server/tests/resume.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
const { chai } = require('meteor/practicalmeteor:chai');
const TestUser = require('../utils/testUser');
const Authenticator = require('../../../server/authenticator').default;
const LoginAttemptGenerator = require('../utils/loginAttemptGenerator')

const expect = chai.expect;
const type = 'resume';
const resume = 'token';
const testUser = new TestUser({
username: 'resume-test',
password: 'resume-test'
});

module.exports = () => {
/**
* Those tests are covering the autologin attempt.
* The attempt is invoked by the core of the meteor accounts.
* Every time a previously logged in user reconnects to the system
* there is a "resume" attempt sent.
*
* This dependency did allow the user to decide during the login
* if he want to have the rememberMe flag set on true or false.
* This setting will have an importance of the decision being made during resume.
*/
describe('resume attempt', () => {
/**
* In case of an user logging in with rememberMe the resume
* attempt should be allowed. This covers a situation where user
* is being logged with rememberMe and then restarts the application.
* The user should stay logged in.
*/
it('should pass if user does have rememberMe', () => {
testUser.setLoginToken({ resume, rememberMe: true });
const loginAttemptGenerator = new LoginAttemptGenerator({ type, resume });
const loginAttempt = loginAttemptGenerator.getLoginAttempt();
const authenticator = new Authenticator(loginAttempt);
const { result, resultCode, reason } = authenticator.validateAttempt();
expect(result).to.be.equal(true);
expect(resultCode).to.be.equal(0);
expect(reason).to.be.equal('Validation passed');
});

/**
* In case of an user logging in without rememberMe the resume
* attempt should not be allowed. This covers a situation where user
* is being logged without rememberMe and then restarts the application.
* The user should be logged out.
*/
it('should not pass if user does not have rememberMe', () => {
testUser.setLoginToken({ resume, rememberMe: false });
const loginAttemptGenerator = new LoginAttemptGenerator({ type, resume });
const loginAttempt = loginAttemptGenerator.getLoginAttempt();
const authenticator = new Authenticator(loginAttempt);
const { result, resultCode, reason } = authenticator.validateAttempt();
expect(result).to.be.equal(false);
expect(resultCode).to.be.equal(-2);
expect(reason).to.be.equal('Resume not allowed when user does not have remember me');
});

/**
* Important thing to keep in mind is that Meteor's login system does not know
* when the user is starting the app from the scratch or just lost the internet.
* It's not intended to logout an user without rememberMe every time he will lose
* the internet connection.
*
* To avoid this situation the user from now is sending also 'loggedAtLeastOnce: true'
* flag if he already logged once in ongoing device session.
*/
describe('connection loss', () => {
/**
* If the user already had a successfull login attempt during his device session
* then he should stay logged in no matter the rememberMe setting after the reconnect.
* Validates if user stays online with rememberMe being set to 'false'.
*/
it('should pass for the same session as previous login when without rememberMe', () => {
testUser.setLoginToken({ resume, rememberMe: false });
const loginAttemptGenerator = new LoginAttemptGenerator({ type, resume });
loginAttemptGenerator.addMethodArgument({ loggedAtLeastOnce: true });
const loginAttempt = loginAttemptGenerator.getLoginAttempt();
const authenticator = new Authenticator(loginAttempt);
const { result, resultCode, reason } = authenticator.validateAttempt();
expect(result).to.be.equal(true);
expect(resultCode).to.be.equal(0);
expect(reason).to.be.equal('Validation passed');
});

/**
* If the user already had a successfull login attempt during his device session
* then he should stay logged in no matter the rememberMe setting after the reconnect.
* Validates if user stays online with rememberMe being set to 'true'.
*/
it('should pass for the same session as previous login when with rememberMe', () => {
testUser.setLoginToken({ resume, rememberMe: true });
const loginAttemptGenerator = new LoginAttemptGenerator({ type, resume });
loginAttemptGenerator.addMethodArgument({ loggedAtLeastOnce: true });
const loginAttempt = loginAttemptGenerator.getLoginAttempt();
const authenticator = new Authenticator(loginAttempt);
const { result, resultCode, reason } = authenticator.validateAttempt();
expect(result).to.be.equal(true);
expect(resultCode).to.be.equal(0);
expect(reason).to.be.equal('Validation passed');
});
});
});
};
Loading

0 comments on commit e8037c9

Please sign in to comment.