Просмотр исходного кода

remove auth, http, storage and token files

pull/342/head
Guillaume Vincent 7 лет назад
Родитель
Сommit
54a8fc533e
13 измененных файлов: 44 добавлений и 495 удалений
  1. +0
    -88
      src/api/auth.js
  2. +0
    -39
      src/api/http.js
  3. +0
    -44
      src/api/storage.js
  4. +0
    -42
      src/api/token.js
  5. +19
    -24
      src/store/actions.js
  6. +1
    -1
      src/store/index.js
  7. +10
    -13
      src/views/PasswordReset.vue
  8. +14
    -18
      src/views/PasswordResetConfirm.vue
  9. +0
    -26
      test/_helpers.js
  10. +0
    -0
      test/api.password.js
  11. +0
    -133
      test/auth.js
  12. +0
    -39
      test/storage.js
  13. +0
    -28
      test/token.js

+ 0
- 88
src/api/auth.js Просмотреть файл

@@ -1,88 +0,0 @@
import axios from 'axios';

export default class Auth {
constructor(storage) {
this.user = {
authenticated: false
};
this.storage = storage;
}

isAuthenticated() {
const token = this.storage.getToken();
if (token.stillValid()) {
this.user.authenticated = true;
return true;
}
this.user.authenticated = false;
return false;
}

isGuest() {
return !this.isAuthenticated()
}

logout() {
return new Promise(resolve => {
this.storage.clear();
this.user.authenticated = false;
resolve();
});
}

login(user, baseURL) {
const config = this.storage.json();
if (baseURL) {
config.baseURL = baseURL;
}
return Auth._requestToken(user, config).then(token => {
this.storage.saveToken(token)
})
}

static _requestToken(user, config = {}) {
return axios.post('/api/tokens/auth/', user, config).then(response => {
return response.data.token;
});
}

refreshToken() {
const config = this.storage.json();
const token = this.storage.getToken();
return Auth._requestNewToken({token: token.name}, config).then(token => {
this.storage.saveToken(token)
});
}

static _requestNewToken(token, config = {}) {
return axios.post('/api/tokens/refresh/', token, config).then(response => {
return response.data.token;
});
}

register(user, baseURL) {
const config = this.storage.json();
if (baseURL) {
config.baseURL = baseURL;
}
return axios.post('/api/auth/register/', user, config).then(response => {
return response.data;
});
}

resetPassword(email, baseURL) {
const config = this.storage.json();
if (baseURL) {
config.baseURL = baseURL;
}
return axios.post('/api/auth/password/reset/', email, config);
}

confirmResetPassword(password, baseURL) {
const config = this.storage.json();
if (baseURL) {
config.baseURL = baseURL;
}
return axios.post('/api/auth/password/reset/confirm/', password, config);
}
}

+ 0
- 39
src/api/http.js Просмотреть файл

@@ -1,39 +0,0 @@
import axios from 'axios';
import {TOKEN_KEY} from './storage';


export default class HTTP {
constructor(resource, storage) {
this.storage = storage;
this.resource = resource;
}

getRequestConfig(params = {}) {
const config = this.storage.json();
return {
...params,
baseURL: config.baseURL,
headers: {Authorization: `JWT ${config[TOKEN_KEY]}`}
};
}

create(resource, params = {}) {
return axios.post('/api/' + this.resource + '/', resource, this.getRequestConfig(params));
}

all(params = {}) {
return axios.get('/api/' + this.resource + '/', this.getRequestConfig(params));
}

get(resource, params = {}) {
return axios.get('/api/' + this.resource + '/' + resource.id + '/', this.getRequestConfig(params));
}

update(resource, params = {}) {
return axios.put('/api/' + this.resource + '/' + resource.id + '/', resource, this.getRequestConfig(params));
}

remove(resource, params = {}) {
return axios.delete('/api/' + this.resource + '/' + resource.id + '/', this.getRequestConfig(params));
}
}

+ 0
- 44
src/api/storage.js Просмотреть файл

@@ -1,44 +0,0 @@
import Token from './token';

export const LOCAL_STORAGE_KEY = 'lesspass';
export const TOKEN_KEY = 'jwt';

export default class Storage {
constructor(storage = window.localStorage) {
this.storage = storage;
}

_getLocalStorage() {
return JSON.parse(this.storage.getItem(LOCAL_STORAGE_KEY) || '{}')
}

json() {
const defaultStorage = {
baseURL: 'https://lesspass.com',
timeout: 5000
};
const localStorage = this._getLocalStorage();
return Object.assign(defaultStorage, localStorage);
}

save(data) {
const newData = Object.assign(this._getLocalStorage(), data);
this.storage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newData));
}

clear() {
this.storage.clear();
}

getToken() {
const storage = this.json();
if (TOKEN_KEY in storage) {
return new Token(storage[TOKEN_KEY]);
}
return new Token();
}

saveToken(token) {
this.save({[TOKEN_KEY]: token})
}
}

+ 0
- 42
src/api/token.js Просмотреть файл

@@ -1,42 +0,0 @@
import jwtDecode from 'jwt-decode';

export const TOKEN_KEY = 'jwt';

export default class Token {
constructor(tokenName) {
this.name = tokenName
}

stillValid(now = new Date()) {
try {
return this._expirationDateSuperiorTo(now);
}
catch (err) {
return false;
}
}

expiresInMinutes(minutes, now = new Date()) {
try {
const nowPlusDuration = new Date(now.getTime() + minutes*60000);
return this._expirationDateInferiorTo(nowPlusDuration);
}
catch (err) {
return false;
}
}

_expirationDateInferiorTo(date) {
const expireDate = this._getTokenExpirationDate();
return expireDate < date;
}

_expirationDateSuperiorTo(date) {
return !this._expirationDateInferiorTo(date)
}

_getTokenExpirationDate() {
const decodedToken = jwtDecode(this.name);
return new Date(decodedToken.exp * 1000);
}
}

+ 19
- 24
src/store/actions.js Просмотреть файл

@@ -1,14 +1,7 @@
import User from '../api/user';
import Storage from '../api/storage';
import Auth from '../api/auth';
import HTTP from '../api/http';
import Password from '../api/password';

import * as types from './mutation-types'

const storage = new Storage();
const auth = new Auth(storage);
const Passwords = new HTTP('passwords', storage);

export const loadPasswordFirstTime = ({commit}) => {
commit(types.LOAD_PASSWORD_FIRST_TIME);
};
@@ -44,19 +37,18 @@ export const login = ({commit}, payload) => {
};

export const logout = ({commit}) => {
auth.logout();
commit(types.LOGOUT);
};

export const getPasswords = ({commit}) => {
if (auth.isAuthenticated()) {
Passwords.all().then(response => commit(types.SET_PASSWORDS, {passwords: response.data.results}));
export const getPasswords = ({commit, state}) => {
if (state.authenticated) {
Password.all(state).then(response => commit(types.SET_PASSWORDS, {passwords: response.data.results}));
}
};

export const getPassword = ({commit}, payload) => {
if (auth.isAuthenticated()) {
Passwords.get(payload).then(response => commit(types.SET_PASSWORD, {password: response.data}));
export const getPassword = ({commit, state}, payload) => {
if (state.authenticated) {
Password.read(payload, state).then(response => commit(types.SET_PASSWORD, {password: response.data}));
}
};

@@ -65,19 +57,22 @@ export const saveOrUpdatePassword = ({commit, state}) => {
const site = state.password.site;
const login = state.password.login;
if (site || login) {
Passwords.create(state.password).then(() => {
getPasswords({commit});
})
Password.create(state.password, state)
.then(() => {
getPasswords({commit});
})
}
} else {
Passwords.update(state.password).then(() => {
getPasswords({commit});
})
Password.update(state.password, state)
.then(() => {
getPasswords({commit});
})
}
};

export const deletePassword = ({commit}, payload) => {
Passwords.remove(payload).then(() => {
commit(types.DELETE_PASSWORD, payload);
});
Password.delete(payload, state)
.then(() => {
commit(types.DELETE_PASSWORD, payload);
});
};

+ 1
- 1
src/store/index.js Просмотреть файл

@@ -34,5 +34,5 @@ export default new Vuex.Store({
getters,
actions,
mutations,
plugins: [createPersistedState({key: 'lesspass-store'})]
plugins: [createPersistedState({key: 'lesspass'})]
});

+ 10
- 13
src/views/PasswordReset.vue Просмотреть файл

@@ -39,17 +39,12 @@
</form>
</template>
<script type="text/ecmascript-6">
import Auth from '../api/auth';
import Storage from '../api/storage';
import User from '../api/user';
import {mapActions, mapGetters} from 'vuex';

export default {
data() {
const storage = new Storage();
const auth = new Auth(storage);
return {
auth,
storage,
email: '',
emailRequired: false,
showError: false,
@@ -77,13 +72,15 @@
return;
}
this.loading = true;
this.auth.resetPassword({email: this.email}).then(()=> {
this.cleanErrors();
this.successMessage = true;
}).catch(() => {
this.cleanErrors();
this.showError = true;
});
User.resetPassword({email: this.email})
.then(() => {
this.cleanErrors();
this.successMessage = true;
})
.catch(() => {
this.cleanErrors();
this.showError = true;
});
}
}
}


+ 14
- 18
src/views/PasswordResetConfirm.vue Просмотреть файл

@@ -39,17 +39,12 @@
</form>
</template>
<script type="text/ecmascript-6">
import Auth from '../api/auth';
import Storage from '../api/storage';
import User from '../api/user';
import {mapActions, mapGetters} from 'vuex';

export default {
data() {
const storage = new Storage();
const auth = new Auth(storage);
return {
auth,
storage,
new_password: '',
passwordRequired: false,
showError: false,
@@ -72,18 +67,19 @@
this.passwordRequired = true;
return;
}
this.auth.confirmResetPassword({
uid: this.$route.params.uid,
token: this.$route.params.token,
new_password: this.new_password
}).then(()=> {
this.successMessage = true
}).catch(err => {
if(err.response.status === 400){
this.errorMessage = 'This password reset link become invalid.'
}
this.showError = true;
});
const resetPassword = {
uid: this.$route.params.uid, token: this.$route.params.token, new_password: this.new_password
};
User.confirmResetPassword(resetPassword)
.then(() => {
this.successMessage = true
})
.catch(err => {
if (err.response.status === 400) {
this.errorMessage = 'This password reset link become invalid.'
}
this.showError = true;
});
}
},
computed: {


+ 0
- 26
test/_helpers.js Просмотреть файл

@@ -1,26 +0,0 @@
export class LocalStorageMock {
constructor(storage = {}) {
this.storage = storage;
}

setItem(key, value) {
this.storage[key] = value || '';
}

getItem(key) {
return this.storage[key] || null;
}

removeItem(key) {
delete this.storage[key];
}

key(i) {
const keys = Object.keys(this.storage);
return keys[i] || null;
}

clear() {
this.storage = {};
}
}

test/password.js → test/api.password.js Просмотреть файл


+ 0
- 133
test/auth.js Просмотреть файл

@@ -1,133 +0,0 @@
import test from 'ava';
import {LocalStorageMock} from './_helpers';
import Auth from '../src/api/auth';
import Storage, {LOCAL_STORAGE_KEY} from '../src/api/storage';
import nock from 'nock';


function AuthFactory(token, localStorage = new LocalStorageMock()) {
const storage = new Storage(localStorage);
storage.saveToken(token);
return new Auth(storage);
}

test('request token', t => {
const token = '5e0651';
const user = {email: 'test@example.org', password: 'password'};
nock('https://lesspass.com').post('/api/tokens/auth/', user).reply(201, {token});
return Auth._requestToken(user, {baseURL: 'https://lesspass.com'}).then(requestedToken => {
t.is(requestedToken, token);
});
});

test('request new token', t => {
const token = '3e3231';
const newToken = 'wibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9eyJzdWIiOiIxMjM0NTY3ODkwIi';
nock('https://lesspass.com').post('/api/tokens/refresh/', {token}).reply(200, {token: newToken});
return Auth._requestNewToken({token}, {baseURL: 'https://lesspass.com'}).then(refreshedToken => {
t.is(refreshedToken, newToken);
});
});

test('user first connection is guest', t => {
const storage = new Storage(new LocalStorageMock());
const auth = new Auth(storage);
t.true(auth.isGuest());
});

test('user return on site before token expire', t => {
const auth = AuthFactory('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE0MzcwMTg1ODIsImV4cCI6MTc1NzkyODQzNH0.KzEBhVgm3xa51jsBklB0Ib9DDwAkvynOnkwLLJoD5AU');
t.true(auth.isAuthenticated());
t.false(auth.isGuest());
});

test('user return on site after token expiration', t => {
const auth = AuthFactory('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE0MzcwMTg1ODIsImV4cCI6MTQzNzAxODU4M30.NmMv7sXjM1dW0eALNXud8LoXknZ0mH14GtnFclwJv0s');
t.false(auth.isAuthenticated());
t.true(auth.isGuest());
t.false(auth.user.authenticated);
});

test('login save token', t => {
const token = '3e3231';
const storage = new LocalStorageMock();
const auth = AuthFactory(token, storage);
const user = {
email: 'test@lesspass.com',
password: 'password'
};
nock('https://lesspass.com').post('/api/tokens/auth/', user).reply(201, {token});
return auth.login(user).then(() => {
t.is(JSON.parse(storage.getItem(LOCAL_STORAGE_KEY)).jwt, token);
});
});

test('logout user remove token and unauthenticate user', t => {
const token = '3e3231';
const storage = new LocalStorageMock();
const auth = AuthFactory(token, storage);
return auth.logout().then(() => {
t.falsy(storage.getItem(LOCAL_STORAGE_KEY));
});
});

test('login custom endpoint', t => {
const token = '3e3231';
const storage = new LocalStorageMock();
const auth = AuthFactory(token, storage);
const user = {
email: 'test@lesspass.com',
password: 'password'
};
nock('https://test.example.org').post('/api/tokens/auth/', user).reply(201, {token});
return auth.login(user, 'https://test.example.org').then(() => {
t.is(JSON.parse(storage.getItem(LOCAL_STORAGE_KEY)).jwt, token);
});
});

test('refresh token', t => {
const token = '3e3231';
const storage = new LocalStorageMock();
const auth = AuthFactory(token, storage);
const newToken = 'wibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9eyJzdWIiOiIxMjM0NTY3ODkwIi';
nock('https://lesspass.com').post('/api/tokens/refresh/', {token}).reply(200, {token: newToken});
return auth.refreshToken().then(() => {
t.is(JSON.parse(storage.getItem(LOCAL_STORAGE_KEY)).jwt, newToken);
});
});

test('should register a user', t => {
const user = {
email: 'test@lesspass.com',
password: 'password'
};
const localStorage = new LocalStorageMock();
const storage = new Storage(localStorage);
const auth = new Auth(storage);
nock('https://lesspass.com').post('/api/auth/register/', user).reply(201, {email: user.email, pk: 1});
return auth.register(user).then(newUser => {
t.is(newUser.email, user.email);
});
});

test('should reset a password', t => {
var email = 'test@lesspass.com';
const localStorage = new LocalStorageMock();
const storage = new Storage(localStorage);
const auth = new Auth(storage);
nock('https://lesspass.com').post('/api/auth/password/reset/', {email}).reply(204);
t.notThrows(auth.resetPassword({email}));
});

test('should confirm reset password', t => {
var newPassword ={
uid: 'MQ',
token: '5g1-2bd69bd6f6dcd73f8124',
new_password: 'password1'
};
const localStorage = new LocalStorageMock();
const storage = new Storage(localStorage);
const auth = new Auth(storage);
nock('https://lesspass.com').post('/api/auth/password/reset/confirm/', newPassword).reply(204);
t.notThrows(auth.confirmResetPassword(newPassword));
});

+ 0
- 39
test/storage.js Просмотреть файл

@@ -1,39 +0,0 @@
import test from 'ava';
import {LocalStorageMock} from './_helpers';
import Storage, {LOCAL_STORAGE_KEY} from '../src/api/storage';

const localStorage = new LocalStorageMock();
const storage = new Storage(localStorage);

test('get default storage', t => {
t.is(storage.json().baseURL, 'https://lesspass.com');
});

test('get storage saved in local storage', t => {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify({baseURL: 'https://example.org'}));
t.is(storage.json().baseURL, 'https://example.org');
});

test('save storage in local storage', t => {
storage.save({baseURL: 'https://example.org'});
t.is(localStorage.getItem(LOCAL_STORAGE_KEY), '{"baseURL":"https://example.org"}');
});

test('save storage in local storage', t => {
storage.save({baseURL: 'https://example.org'});
t.is(localStorage.getItem(LOCAL_STORAGE_KEY), '{"baseURL":"https://example.org"}');
});

test('save storage in local storage merge', t => {
localStorage.clear();
storage.save({a: 'a'});
storage.save({b: 'b'});
t.is(localStorage.getItem(LOCAL_STORAGE_KEY), '{"a":"a","b":"b"}');
});

test('storage clear local storage', t => {
storage.save({a: 'a'});
storage.clear();
t.is(localStorage.getItem(LOCAL_STORAGE_KEY), null);
});


+ 0
- 28
test/token.js Просмотреть файл

@@ -1,28 +0,0 @@
import test from 'ava';
import Token from '../src/api/token';

test('token is near the end', t => {
const token = new Token('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE0MzcwMTg1ODIsImV4cCI6MTQzNzAxODU4M30.NmMv7sXjM1dW0eALNXud8LoXknZ0mH14GtnFclwJv0s');
t.true(token.expiresInMinutes(15, new Date(1437018283 * 1000)));
t.false(token.expiresInMinutes(5, new Date(1437018283 * 1000)));
});

test('token still valid', t => {
const token = new Token('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE0MzcwMTg1ODIsImV4cCI6MTc1NzkyODQzNH0.KzEBhVgm3xa51jsBklB0Ib9DDwAkvynOnkwLLJoD5AU');
t.true(token.stillValid());
});

test('token still valid check payload date', t => {
const token = new Token('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE0MzcwMTg1ODIsImV4cCI6MTQzNzAxODU4M30.NmMv7sXjM1dW0eALNXud8LoXknZ0mH14GtnFclwJv0s');
t.true(token.stillValid(new Date(1437018283 * 1000)));
});

test('token expired', t => {
const token = new Token('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE0MzcwMTg1ODIsImV4cCI6MTQzNzAxODU4M30.NmMv7sXjM1dW0eALNXud8LoXknZ0mH14GtnFclwJv0s');
t.false(token.stillValid());
});

test('token invalid does not raise an error', t => {
const token = new Token('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9');
t.false(token.stillValid());
});

Загрузка…
Отмена
Сохранить