Add user storage and db-based login

keep-around/dc735e80914fa50c9bca8f6c78c100b7ed19408f
Florian Hartwich 2017-03-13 04:12:18 +01:00
parent b880e2d062
commit f1f03c9b69
11 changed files with 114 additions and 43 deletions

View File

@ -29,17 +29,29 @@
"assignee": {}, "assignee": {},
"id": 5, "id": 5,
"state": "IN_PROGRESS" "state": "IN_PROGRESS"
}
],
"users": [
{
"id": 301,
"name": "admin",
"password": "admin",
"edit_tasks": true,
"change_settings": true
}, },
{ {
"assignee": { "id" : 302,
"name": "hardi", "name": "user_edit",
"email": "hardi@noarch.de" "password": "secret",
}, "edit_tasks": true,
"tags": [], "change_settings": false
"state": "IN_PROGRESS", },
"title": "Testplan schreiben", {
"description": "Testplan für dieses Projekt anlegen", "id" : 303,
"id": 6 "name": "user",
"password": "secret",
"edit_tasks": false,
"change_settings": false
} }
] ]
} }

View File

@ -65,7 +65,7 @@ if (secured) {
var password = req.body.password; var password = req.body.password;
var user = { var user = {
name: req.body.name, name: req.body.name,
passowrd: req.body.password password: req.body.password
}; };
if (password === "s3cret") { if (password === "s3cret") {
var token = jwt.sign(user, server.get('superSecret'), { var token = jwt.sign(user, server.get('superSecret'), {

View File

@ -6,7 +6,8 @@ import {HttpModule} from '@angular/http';
import {AppComponent} from './app.component'; import {AppComponent} from './app.component';
import {TaskService} from "./services/task-service/task.service"; import {TaskService} from "./services/task-service/task.service";
import {LoginService} from "./services/login-service/login-service"; import {LoginService} from "./services/login-service/login-service";
import {TaskStore} from './services/stores/task.store'; import {UserStore} from "./services/stores/user.store";
import {TaskStore} from "./services/stores/task.store";
import {ShowErrorComponent} from './show-error/show-error.component'; import {ShowErrorComponent} from './show-error/show-error.component';
import {APPLICATION_VALIDATORS} from './models/app-validators'; import {APPLICATION_VALIDATORS} from './models/app-validators';
import {appRouting, routingComponents, routingProviders} from './app.routing'; import {appRouting, routingComponents, routingProviders} from './app.routing';
@ -28,6 +29,7 @@ const enableAuthentication = !environment.e2eMode;
@NgModule({ @NgModule({
imports: [BrowserModule, FormsModule, ReactiveFormsModule, appRouting, HttpModule], imports: [BrowserModule, FormsModule, ReactiveFormsModule, appRouting, HttpModule],
providers: [LoginService, providers: [LoginService,
UserStore,
TaskService, TaskService,
TaskStore, TaskStore,
Title, Title,
@ -43,4 +45,4 @@ const enableAuthentication = !environment.e2eMode;
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule { export class AppModule {
} }

View File

@ -1,7 +1,14 @@
export interface User {
id?: number;
name?: string;
password?: string;
edit_tasks?: boolean;
change_settings?: boolean;
}
export interface Tag { export interface Tag {
label: string; label: string;
} }
export interface User { export interface Assignee {
name?: string; name?: string;
email?: string; email?: string;
} }
@ -12,7 +19,7 @@ export interface Task {
tags?: Tag[]; tags?: Tag[];
favorite?: boolean; favorite?: boolean;
state?: string; state?: string;
assignee?: User; assignee?: Assignee;
} }
export const states = ['BACKLOG', 'IN_PROGRESS', 'TEST', 'COMPLETED']; export const states = ['BACKLOG', 'IN_PROGRESS', 'TEST', 'COMPLETED'];

View File

@ -1,23 +1,48 @@
import {Inject, Optional} from '@angular/core' import {Injectable, Inject, Optional} from '@angular/core';
import {AUTH_ENABLED} from '../../app.tokens'; import {User} from '../../models/model-interfaces';
import {Http, URLSearchParams, RequestMethod, RequestOptions} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {LOAD, ADD, EDIT, REMOVE, UserStore} from '../stores/user.store';
import {SOCKET_IO, AUTH_ENABLED} from '../../app.tokens';
const BASE_URL = `http://localhost:3000/api/users`;
const WEB_SOCKET_URL = 'http://localhost:3001';
const CURRENT_USER = 'currentUser'; const CURRENT_USER = 'currentUser';
@Injectable()
export class LoginService { export class LoginService {
USERS = [ socket: SocketIOClient.Socket;
{name: 'admin', password: 'admin', rights: ['edit_tasks', 'change_settings'] },
{name: 'user', password: 'secret', rights: ['edit_tasks'] }
];
constructor(@Optional() @Inject(AUTH_ENABLED) public authEnabled = false) { users$: Observable<User[]>;
results$: User[];
constructor(@Optional() @Inject(AUTH_ENABLED) public authEnabled = false, private http: Http, private userStore: UserStore,
@Inject(SOCKET_IO) socketIO) {
this.users$ = userStore.items$;
this.socket = socketIO(WEB_SOCKET_URL);
Observable.fromEvent(this.socket, 'user_saved')
.subscribe((action) => {
this.userStore.dispatch(action);
});
}
getUser(name: string) : User[]{
this.http.get(BASE_URL + "?name=" + name).subscribe(result => this.results$ = result.json());
return this.results$;
} }
login(name, password) { login(name, password) {
const [user] = this.USERS.filter(user => user.name == name); if (this.getUser(name)) {
if (user && user.password === password) { let user = this.results$[0];
localStorage.setItem(CURRENT_USER, JSON.stringify(user)); if (user && user.password === password) {
return true; localStorage.setItem(CURRENT_USER, JSON.stringify(user));
return true;
}
} }
} }
@ -29,11 +54,4 @@ export class LoginService {
return !this.authEnabled || localStorage.getItem(CURRENT_USER) != null; return !this.authEnabled || localStorage.getItem(CURRENT_USER) != null;
} }
getUser() {
const userString = localStorage.getItem(CURRENT_USER);
if (userString) {
return JSON.parse(userString);
}
}
} }

View File

@ -1 +0,0 @@
export * from "./task.store";

View File

@ -1,6 +0,0 @@
import {Store} from "./generic-store";
import {Task} from "../../models/model-interfaces";
export class TaskStore extends Store<Task> {
}

View File

@ -36,4 +36,4 @@ export class TaskStore {
return tasks; return tasks;
} }
} }
} }

View File

@ -0,0 +1,39 @@
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {User} from '../../models/model-interfaces';
export const LOAD = 'LOAD';
export const ADD = 'ADD';
export const EDIT = 'EDIT';
export const REMOVE = 'REMOVE';
export class UserStore {
private users: User[] = [];
items$ = new BehaviorSubject<User[]>([]);
dispatch(action) {
console.log("dispatched")
this.users = this._reduce(this.users, action);
this.items$.next(this.users);
}
_reduce(users: User[], action) {
switch (action.type) {
case LOAD:
return [...action.data];
case ADD:
return [...users, action.data];
case EDIT:
return users.map(user => {
const editedUser = action.data;
if (user.id !== editedUser.id){
return user;
}
return editedUser;
});
case REMOVE:
return users.filter(user => user.id !== action.data.id);
default:
return users;
}
}
}

View File

@ -87,4 +87,4 @@ describe('Task-Service', () => {
expect(dispatchedAction.data.title).toEqual('Task 1'); expect(dispatchedAction.data.title).toEqual('Task 1');
})); }));
}); });

View File

@ -3,7 +3,7 @@ import {Task} from '../../models/model-interfaces';
import {Http, URLSearchParams, RequestMethod, RequestOptions} from '@angular/http'; import {Http, URLSearchParams, RequestMethod, RequestOptions} from '@angular/http';
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {LOAD, ADD, EDIT, REMOVE, TaskStore} from '../stores/index'; import {LOAD, ADD, EDIT, REMOVE, TaskStore} from '../stores/task.store';
import {SOCKET_IO} from '../../app.tokens'; import {SOCKET_IO} from '../../app.tokens';
const BASE_URL = `http://localhost:3000/api/tasks/`; const BASE_URL = `http://localhost:3000/api/tasks/`;