diff --git a/wg_dashboard_backend/db/user.py b/wg_dashboard_backend/db/user.py
index d766d7f..e7b7513 100644
--- a/wg_dashboard_backend/db/user.py
+++ b/wg_dashboard_backend/db/user.py
@@ -1,7 +1,11 @@
+from typing import Optional
+
from sqlalchemy.orm import Session
import models
from passlib.context import CryptContext
+import schemas
+
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
@@ -13,13 +17,22 @@ def get_password_hash(password):
return pwd_context.hash(password)
-def authenticate_user(sess, username: str, password: str):
+def update_user(sess: Session, form_data: schemas.UserInDB):
+ user = get_user_by_name(sess, form_data.username)
+ user.password = form_data.password
+ user.full_name = form_data.full_name
+ user.email = form_data.email # TOD this section should be updated
+
+ sess.add(user)
+ sess.commit()
+ return get_user_by_name(sess, form_data.username)
+
+
+def authenticate_user(sess, username: str, password: str) -> Optional[models.User]:
user = get_user_by_name(sess, username)
- if not user:
- return False
- if not verify_password(password, user.password):
- return False
- return user
+ if user and verify_password(password, user.password):
+ return user
+ return None
def get_user_by_name(db: Session, username: str) -> models.User:
diff --git a/wg_dashboard_backend/main.py b/wg_dashboard_backend/main.py
index 5257c5a..f53a0eb 100644
--- a/wg_dashboard_backend/main.py
+++ b/wg_dashboard_backend/main.py
@@ -1,6 +1,7 @@
import logging
+import os
-from sqlalchemy.exc import OperationalError
+from sqlalchemy_utils import database_exists
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
@@ -21,7 +22,7 @@ from datetime import datetime, timedelta
import db.wireguard
import db.user
import jwt
-from fastapi import Depends, FastAPI, HTTPException, status, Form
+from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt import PyJWTError
import script.wireguard
@@ -35,16 +36,24 @@ engine = sqlalchemy.create_engine(
const.DATABASE_URL, connect_args={"check_same_thread": False}
)
-try:
- models.Base.metadata.create_all(engine)
-except OperationalError as e:
- pass
-
-oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/token")
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
-app = FastAPI()
+if not database_exists(engine.url):
+ models.Base.metadata.create_all(engine)
+ # Create default user
+ _db: Session = SessionLocal()
+ _db.add(models.User(
+ username=os.getenv("ADMIN_USERNAME", "admin"),
+ password=db.user.get_password_hash(os.getenv("ADMIN_PASSWORD", "admin")),
+ full_name="Admin",
+ role="admin",
+ email=""
+ ))
+ _db.commit()
+ _db.close()
+app = FastAPI()
# Dependency
def get_db():
@@ -66,28 +75,45 @@ def create_access_token(*, data: dict, expires_delta: timedelta = None):
return encoded_jwt
-def get_current_user(token: str = Depends(oauth2_scheme), sess: Session = Depends(get_db)):
+def auth(token: str = Depends(oauth2_scheme), sess: Session = Depends(get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
+
try:
payload = jwt.decode(token, const.SECRET_KEY, algorithms=[const.ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
- token_data = schemas.TokenData(username=username)
+
except PyJWTError:
raise credentials_exception
- user = db.user.get_user_by_name(sess, token_data.username)
+ user = db.user.get_user_by_name(sess, username)
if user is None:
raise credentials_exception
return user
-@app.post("/api/token", response_model=schemas.Token)
-def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), sess: Session = Depends(get_db)):
+@app.get("/api/logout")
+def logout(user: schemas.User = Depends(auth)):
+ # TODO
+ return {}
+
+
+@app.post("/api/user/edit", response_model=schemas.User)
+def edit(form_data: schemas.UserInDB, user: schemas.User = Depends(auth), sess: Session = Depends(get_db)):
+
+ form_data.password = db.user.get_password_hash(form_data.password)
+
+ db_user = db.user.update_user(sess, form_data)
+
+ return schemas.User.from_orm(db_user)
+
+
+@app.post("/api/login", response_model=schemas.Token)
+def login(form_data: OAuth2PasswordRequestForm = Depends(), sess: Session = Depends(get_db)):
user = db.user.authenticate_user(sess, form_data.username, form_data.password)
if not user:
@@ -96,17 +122,23 @@ def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), ses
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
+
+ # Create token
access_token_expires = timedelta(minutes=const.ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
- return {"access_token": access_token, "token_type": "bearer"}
+
+ return {"access_token": access_token, "token_type": "bearer", "user": schemas.User.from_orm(user)}
# @app.post("/wg/update/", response_model=List[schemas.WireGuard])
@app.get("/api/wg/server/all", response_model=typing.List[schemas.WGServer])
-def get_interfaces(sess: Session = Depends(get_db)):
+def get_interfaces(
+ sess: Session = Depends(get_db),
+ user: schemas.User = Depends(auth)
+):
interfaces = db.wireguard.server_get_all(sess)
for iface in interfaces:
iface.is_running = script.wireguard.is_running(iface)
@@ -115,7 +147,11 @@ def get_interfaces(sess: Session = Depends(get_db)):
@app.post("/api/wg/server/add", response_model=schemas.WGServer)
-def add_interface(form_data: schemas.WGServer, sess: Session = Depends(get_db)):
+def add_interface(
+ form_data: schemas.WGServer,
+ sess: Session = Depends(get_db),
+ user: schemas.User = Depends(auth)
+):
if form_data.interface is None or form_data.listen_port is None or form_data.address is None:
raise HTTPException(status_code=400,
detail="Interface, Listen-Port and Address must be included in the schema.")
@@ -136,7 +172,10 @@ def add_interface(form_data: schemas.WGServer, sess: Session = Depends(get_db)):
@app.post("/api/wg/server/edit", response_model=schemas.WGServer)
-def edit_server(data: dict, sess: Session = Depends(get_db)):
+def edit_server(
+ data: dict, sess: Session = Depends(get_db),
+ user: schemas.User = Depends(auth)
+):
interface = data["interface"]
server = schemas.WGServer(**data["server"])
@@ -156,7 +195,7 @@ def edit_server(data: dict, sess: Session = Depends(get_db)):
@app.get("/api/wg/generate_keypair", response_model=schemas.KeyPair)
-def generate_key_pair():
+def generate_key_pair(user: schemas.User = Depends(auth)):
private_key, public_key = script.wireguard.generate_keys()
return schemas.KeyPair(
private_key=private_key,
@@ -165,21 +204,28 @@ def generate_key_pair():
@app.get("/api/wg/generate_psk", response_model=schemas.PSK)
-def generate_psk():
+def generate_psk(user: schemas.User = Depends(auth)):
return schemas.PSK(
psk=script.wireguard.generate_psk()
)
@app.post("/api/wg/server/stop", response_model=schemas.WGServer)
-def start_server(form_data: schemas.WGServer, ):
+def start_server(
+ form_data: schemas.WGServer,
+ user: schemas.User = Depends(auth)
+):
script.wireguard.stop_interface(form_data)
form_data.is_running = script.wireguard.is_running(form_data)
return form_data
@app.post("/api/wg/server/start", response_model=schemas.WGServer)
-def start_server(form_data: schemas.WGServer, sess: Session = Depends(get_db)):
+def start_server(
+ form_data: schemas.WGServer,
+ sess: Session = Depends(get_db),
+ user: schemas.User = Depends(auth)
+):
db.wireguard.server_generate_config(sess, form_data)
script.wireguard.start_interface(form_data)
form_data.is_running = script.wireguard.is_running(form_data)
@@ -187,7 +233,11 @@ def start_server(form_data: schemas.WGServer, sess: Session = Depends(get_db)):
@app.post("/api/wg/server/restart", response_model=schemas.WGServer)
-def start_server(form_data: schemas.WGServer, sess: Session = Depends(get_db)):
+def start_server(
+ form_data: schemas.WGServer,
+ sess: Session = Depends(get_db),
+ user: schemas.User = Depends(auth)
+):
db.wireguard.server_generate_config(sess, form_data)
script.wireguard.restart_interface(form_data)
form_data.is_running = script.wireguard.is_running(form_data)
@@ -195,7 +245,11 @@ def start_server(form_data: schemas.WGServer, sess: Session = Depends(get_db)):
@app.post("/api/wg/server/delete", response_model=schemas.WGServer)
-def delete_server(form_data: schemas.WGServer, sess: Session = Depends(get_db)):
+def delete_server(
+ form_data: schemas.WGServer,
+ sess: Session = Depends(get_db),
+ user: schemas.User = Depends(auth)
+):
# Stop if running
if script.wireguard.is_running(form_data):
script.wireguard.stop_interface(form_data)
@@ -206,7 +260,11 @@ def delete_server(form_data: schemas.WGServer, sess: Session = Depends(get_db)):
@app.post("/api/wg/server/peer/add", response_model=schemas.WGPeer)
-def add_peer(form_data: schemas.WGServer, sess: Session = Depends(get_db)):
+def add_peer(
+ form_data: schemas.WGServer,
+ sess: Session = Depends(get_db),
+ user: schemas.User = Depends(auth)
+):
wg_peer = schemas.WGPeer(server=form_data.interface)
# Insert initial peer
@@ -222,7 +280,11 @@ def add_peer(form_data: schemas.WGServer, sess: Session = Depends(get_db)):
@app.post("/api/wg/server/peer/delete", response_model=schemas.WGPeer)
-def delete_peer(form_data: schemas.WGPeer, sess: Session = Depends(get_db)):
+def delete_peer(
+ form_data: schemas.WGPeer,
+ sess: Session = Depends(get_db),
+ user: schemas.User = Depends(auth)
+):
if not db.wireguard.peer_remove(sess, form_data):
raise HTTPException(400, detail="Were not able to delete peer %s (%s)" % (form_data.name, form_data.public_key))
@@ -234,7 +296,11 @@ def delete_peer(form_data: schemas.WGPeer, sess: Session = Depends(get_db)):
@app.post("/api/wg/server/peer/edit", response_model=schemas.WGPeer)
-def edit_peer(form_data: schemas.WGPeer, sess: Session = Depends(get_db)):
+def edit_peer(
+ form_data: schemas.WGPeer,
+ sess: Session = Depends(get_db),
+ user: schemas.User = Depends(auth)
+):
wg_peer = db.wireguard.peer_update(sess, form_data)
db.wireguard.peer_generate_config(sess, wg_peer)
@@ -242,13 +308,20 @@ def edit_peer(form_data: schemas.WGPeer, sess: Session = Depends(get_db)):
@app.post("/api/wg/server/stats")
-def edit_peer(form_data: schemas.WGServer):
+def edit_peer(
+ form_data: schemas.WGServer,
+ user: schemas.User = Depends(auth)
+):
stats = script.wireguard.get_stats(form_data)
return JSONResponse(content=stats)
@app.post("/api/wg/server/peer/config", response_model=schemas.WGPeerConfig)
-def config_peer(form_data: schemas.WGPeer, sess: Session = Depends(get_db)):
+def config_peer(
+ form_data: schemas.WGPeer,
+ sess: Session = Depends(get_db),
+ user: schemas.User = Depends(auth)
+):
db_peer = db.wireguard.peer_query_get_by_address(sess, form_data.address, form_data.server).one()
with open(const.PEER_FILE(db_peer), "r") as f:
@@ -258,7 +331,10 @@ def config_peer(form_data: schemas.WGPeer, sess: Session = Depends(get_db)):
@app.post("/api/wg/server/config", response_model=schemas.WGPeerConfig)
-def config_server(form_data: schemas.WGServer):
+def config_server(
+ form_data: schemas.WGServer,
+ user: schemas.User = Depends(auth)
+):
with open(const.SERVER_FILE(form_data.interface), "r") as f:
conf_file = f.read()
@@ -266,7 +342,11 @@ def config_server(form_data: schemas.WGServer):
@app.post("/api/users/create/")
-def create_user(form_data: schemas.UserInDB, sess: Session = Depends(get_db)):
+def create_user(
+ form_data: schemas.UserInDB,
+ sess: Session = Depends(get_db),
+ user: schemas.User = Depends(auth)
+):
user = db.user.get_user_by_name(sess, form_data.username)
# User already exists
@@ -293,6 +373,7 @@ def create_user(form_data: schemas.UserInDB, sess: Session = Depends(get_db)):
), sess)
+
@app.on_event("startup")
async def startup():
await database.connect()
diff --git a/wg_dashboard_backend/schemas.py b/wg_dashboard_backend/schemas.py
index a12283d..7d60b32 100644
--- a/wg_dashboard_backend/schemas.py
+++ b/wg_dashboard_backend/schemas.py
@@ -3,21 +3,15 @@ from pydantic import BaseModel, typing
import models
-class Token(BaseModel):
- access_token: str
- token_type: str
-
-
-class TokenData(BaseModel):
- username: str = None
-
-
class User(BaseModel):
username: str = None
email: str = None
full_name: str = None
role: str = None
+ class Config:
+ orm_mode = True
+
class UserInDB(User):
password: str
@@ -26,6 +20,15 @@ class UserInDB(User):
orm_mode = True
+class Token(BaseModel):
+ access_token: str
+ token_type: str
+ user: User
+
+ class Config:
+ orm_mode = True
+
+
class WGPeer(BaseModel):
name: str = None
address: str = None
diff --git a/wg_dashboard_frontend/src/app/app-routing.module.ts b/wg_dashboard_frontend/src/app/app-routing.module.ts
index f613c40..53a5223 100644
--- a/wg_dashboard_frontend/src/app/app-routing.module.ts
+++ b/wg_dashboard_frontend/src/app/app-routing.module.ts
@@ -4,7 +4,11 @@ import { RouterModule } from '@angular/router';
import { LayoutsModule } from './layouts';
import { CommonLayoutComponent } from './layouts/common-layout';
import { DashboardComponent } from './pages/dashboard';
-import {LoginComponent} from "./pages/pages/login";
+
+import {AuthGuard} from "@services/*";
+import {EditComponent} from "./pages/user/edit/edit.component";
+import {LoginComponent} from "./pages/user/login/login.component";
+
@NgModule({
@@ -14,12 +18,21 @@ import {LoginComponent} from "./pages/pages/login";
{ path: '', redirectTo: 'app/dashboard', pathMatch: 'full' },
{ path: 'app', component: CommonLayoutComponent, children:
[
- { path: 'dashboard', component: DashboardComponent, pathMatch: 'full'}, // canActivate: [AuthGuard]
+ { path: 'dashboard', component: DashboardComponent, pathMatch: 'full', canActivate: [AuthGuard]},
+
+
+ { path: '**', redirectTo: '/pages/404'},
+ ]
+ },
+
+ { path: 'user', component: CommonLayoutComponent, children:
+ [
{ path: 'login', component: LoginComponent, pathMatch: 'full'},
- { path: '**', redirectTo: '/pages/404' },
+ { path: 'edit', component: EditComponent, pathMatch: 'full', canActivate: [AuthGuard]},
]
},
- { path: 'pages', loadChildren: () => import('./pages/pages/pages.module').then(m => m.PagesModule) },
+
+
{ path: '**', redirectTo: '/pages/404' },
],
{ useHash: true },
diff --git a/wg_dashboard_frontend/src/app/app.component.ts b/wg_dashboard_frontend/src/app/app.component.ts
index 5f0bca1..339dc7e 100644
--- a/wg_dashboard_frontend/src/app/app.component.ts
+++ b/wg_dashboard_frontend/src/app/app.component.ts
@@ -1,7 +1,15 @@
import { Component } from '@angular/core';
+import {AuthService} from "@services/*";
@Component({
selector: 'app-root',
template: ``,
})
-export class AppComponent { }
+export class AppComponent {
+
+ constructor(private auth: AuthService) {
+ auth.init()
+ }
+
+
+}
diff --git a/wg_dashboard_frontend/src/app/app.module.ts b/wg_dashboard_frontend/src/app/app.module.ts
index d586914..c9b7489 100644
--- a/wg_dashboard_frontend/src/app/app.module.ts
+++ b/wg_dashboard_frontend/src/app/app.module.ts
@@ -2,7 +2,7 @@ import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
-import { AuthInterceptor, AuthService, FakeBackendInterceptor } from '@services/*';
+import { AuthInterceptor, AuthService } from '@services/*';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@@ -11,6 +11,7 @@ import { DashboardModule } from './pages/dashboard';
import { VarDirective } from './directives/var.directive';
import { QRCodeModule } from 'angularx-qrcode';
import {NgbModule} from "@ng-bootstrap/ng-bootstrap";
+import {UserModule} from "./pages/user/user.module";
@NgModule({
declarations: [AppComponent, VarDirective],
@@ -19,6 +20,7 @@ import {NgbModule} from "@ng-bootstrap/ng-bootstrap";
AppRoutingModule,
ComponentsModule,
DashboardModule,
+ UserModule,
HttpClientModule,
NgbModule,
QRCodeModule
@@ -29,12 +31,7 @@ import {NgbModule} from "@ng-bootstrap/ng-bootstrap";
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true,
- },
- {
- provide: HTTP_INTERCEPTORS,
- useClass: FakeBackendInterceptor,
- multi: true,
- },
+ }
],
bootstrap: [AppComponent],
exports: [
diff --git a/wg_dashboard_frontend/src/app/interfaces/user.ts b/wg_dashboard_frontend/src/app/interfaces/user.ts
new file mode 100644
index 0000000..47b6c30
--- /dev/null
+++ b/wg_dashboard_frontend/src/app/interfaces/user.ts
@@ -0,0 +1,11 @@
+import {Peer} from "./peer";
+
+export interface User {
+ full_name: string;
+ email: string;
+ role: string;
+ username: string;
+ access_token: string,
+ token_type: string,
+
+}
diff --git a/wg_dashboard_frontend/src/app/layouts/common-layout/common-layout.component.html b/wg_dashboard_frontend/src/app/layouts/common-layout/common-layout.component.html
index 8eaf86b..2a5a280 100644
--- a/wg_dashboard_frontend/src/app/layouts/common-layout/common-layout.component.html
+++ b/wg_dashboard_frontend/src/app/layouts/common-layout/common-layout.component.html
@@ -26,28 +26,28 @@
-
-
Logged in as Admin
+
+ Logged in as {{auth.user?.username}}
-
+
- -->
+
diff --git a/wg_dashboard_frontend/src/app/layouts/common-layout/common-layout.component.ts b/wg_dashboard_frontend/src/app/layouts/common-layout/common-layout.component.ts
index 99d2fc9..57c8e4c 100644
--- a/wg_dashboard_frontend/src/app/layouts/common-layout/common-layout.component.ts
+++ b/wg_dashboard_frontend/src/app/layouts/common-layout/common-layout.component.ts
@@ -10,22 +10,19 @@ import { AuthService } from '@services/*';
export class CommonLayoutComponent implements OnInit {
public title = 'Wireguard Manager';
public menu = [
- { name: 'Dashboard', link: 'dashboard', icon: 'dashboard' },
+ { name: 'Dashboard', link: '/app/dashboard', icon: 'dashboard' },
];
- public user;
- constructor(private authService: AuthService,
- private router: Router) {}
+
+ constructor(public auth: AuthService,
+ public router: Router) {}
public ngOnInit() {
- this.authService.userData.subscribe(user => this.user = user ? user : {
- username: 'Luke',
- email: 'Luke@skywalker.com',
- });
+
}
public logout() {
- this.authService.logout()
- .subscribe(res => this.router.navigate(['/pages/login']));
+ this.auth.logout()
+ .subscribe(res => this.router.navigate(['/user/login']));
}
}
diff --git a/wg_dashboard_frontend/src/app/pages/pages/login/index.ts b/wg_dashboard_frontend/src/app/pages/pages/login/index.ts
deleted file mode 100644
index a40d447..0000000
--- a/wg_dashboard_frontend/src/app/pages/pages/login/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { LoginComponent } from './login.component';
diff --git a/wg_dashboard_frontend/src/app/pages/pages/login/login.component.html b/wg_dashboard_frontend/src/app/pages/pages/login/login.component.html
deleted file mode 100644
index cb18206..0000000
--- a/wg_dashboard_frontend/src/app/pages/pages/login/login.component.html
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
Login to Wireguard Manager
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/wg_dashboard_frontend/src/app/pages/pages/pages.module.ts b/wg_dashboard_frontend/src/app/pages/pages/pages.module.ts
index db648d6..28816ce 100644
--- a/wg_dashboard_frontend/src/app/pages/pages/pages.module.ts
+++ b/wg_dashboard_frontend/src/app/pages/pages/pages.module.ts
@@ -7,10 +7,11 @@ import { ThemeModule } from 'theme';
import { TooltipModule } from '../../../theme/directives/tooltip/tooltip.module';
import { ErrorComponent } from './error';
import { ForgotPasswordComponent } from './forgot-password';
-import { LoginComponent } from './login';
+
import { PagesRoutingModule } from './pages-routing.module';
import { SignUpComponent } from './sign-up';
+
@NgModule({
imports: [
CommonModule,
@@ -22,7 +23,6 @@ import { SignUpComponent } from './sign-up';
],
declarations: [
ErrorComponent,
- LoginComponent,
SignUpComponent,
ForgotPasswordComponent,
],
diff --git a/wg_dashboard_frontend/src/app/pages/user/edit/edit.component.html b/wg_dashboard_frontend/src/app/pages/user/edit/edit.component.html
new file mode 100644
index 0000000..6e2720c
--- /dev/null
+++ b/wg_dashboard_frontend/src/app/pages/user/edit/edit.component.html
@@ -0,0 +1,48 @@
+
+
+
+
+ Edit User
+
+
+
+
+
+
+
+
+
+
diff --git a/wg_dashboard_frontend/src/app/pages/user/edit/edit.component.scss b/wg_dashboard_frontend/src/app/pages/user/edit/edit.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/wg_dashboard_frontend/src/app/pages/user/edit/edit.component.ts b/wg_dashboard_frontend/src/app/pages/user/edit/edit.component.ts
new file mode 100644
index 0000000..5cd430a
--- /dev/null
+++ b/wg_dashboard_frontend/src/app/pages/user/edit/edit.component.ts
@@ -0,0 +1,51 @@
+import { Component, OnInit } from '@angular/core';
+import {FormControl, FormGroup, Validators} from "@angular/forms";
+import {AuthService} from "@services/*";
+import {Router} from "@angular/router";
+
+@Component({
+ selector: 'app-edit',
+ templateUrl: './edit.component.html',
+ styleUrls: ['./edit.component.scss']
+})
+export class EditComponent implements OnInit {
+
+ public editForm: FormGroup = new FormGroup({
+ full_name: new FormControl(''),
+ password: new FormControl('', Validators.required),
+ email: new FormControl('', [
+ Validators.required,
+ Validators.pattern('^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$'),
+ Validators.maxLength(20),
+ ]),
+ username: new FormControl('', [Validators.required, Validators.maxLength(20)]),
+ });
+ public user: any;
+ public error: string;
+
+ constructor(private authService: AuthService,
+ private router: Router) {
+
+ }
+
+ public ngOnInit() {
+ this.user = this.authService.user;
+
+
+ this.editForm.setValue({
+ full_name: this.user.full_name,
+ password: "",
+ email: this.user.email,
+ username: this.user.username
+ })
+ }
+
+ public edit() {
+ if (this.editForm.valid) {
+ this.authService.edit(this.editForm.getRawValue())
+ .subscribe(res => this.router.navigate(['/app/dashboard']),
+ error => this.error = error.message);
+ }
+ }
+
+}
diff --git a/wg_dashboard_frontend/src/app/pages/user/login/login.component.html b/wg_dashboard_frontend/src/app/pages/user/login/login.component.html
new file mode 100644
index 0000000..ea2cfce
--- /dev/null
+++ b/wg_dashboard_frontend/src/app/pages/user/login/login.component.html
@@ -0,0 +1,40 @@
+
+
+
+
+ Edit User
+
+
+
+
+
+
+
+
+
+
diff --git a/wg_dashboard_frontend/src/app/pages/user/login/login.component.scss b/wg_dashboard_frontend/src/app/pages/user/login/login.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/wg_dashboard_frontend/src/app/pages/user/login/login.component.spec.ts b/wg_dashboard_frontend/src/app/pages/user/login/login.component.spec.ts
new file mode 100644
index 0000000..d6d85a8
--- /dev/null
+++ b/wg_dashboard_frontend/src/app/pages/user/login/login.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LoginComponent } from './login.component';
+
+describe('LoginComponent', () => {
+ let component: LoginComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ LoginComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LoginComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/wg_dashboard_frontend/src/app/pages/pages/login/login.component.ts b/wg_dashboard_frontend/src/app/pages/user/login/login.component.ts
similarity index 58%
rename from wg_dashboard_frontend/src/app/pages/pages/login/login.component.ts
rename to wg_dashboard_frontend/src/app/pages/user/login/login.component.ts
index 9113d41..0cfee7b 100644
--- a/wg_dashboard_frontend/src/app/pages/pages/login/login.component.ts
+++ b/wg_dashboard_frontend/src/app/pages/user/login/login.component.ts
@@ -1,37 +1,32 @@
import { Component, OnInit } from '@angular/core';
-import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
-import { Router } from '@angular/router';
-
-
-import { BlankLayoutCardComponent } from 'app/components/blank-layout-card';
+import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
import {AuthService} from "@services/*";
+import {Router} from "@angular/router";
@Component({
selector: 'app-login',
- styleUrls: ['../../../components/blank-layout-card/blank-layout-card.component.scss'],
templateUrl: './login.component.html',
+ styleUrls: ['./login.component.scss']
})
-export class LoginComponent extends BlankLayoutCardComponent implements OnInit {
+export class LoginComponent implements OnInit {
+
public loginForm: FormGroup;
- public email;
+ public username;
public password;
- public emailPattern = '^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$';
public error: string;
constructor(private authService: AuthService,
private fb: FormBuilder,
private router: Router) {
- super();
+
this.loginForm = this.fb.group({
password: new FormControl('', Validators.required),
- email: new FormControl('', [
+ username: new FormControl('', [
Validators.required,
- Validators.pattern(this.emailPattern),
- Validators.maxLength(20),
]),
});
- this.email = this.loginForm.get('email');
+ this.username = this.loginForm.get('username');
this.password = this.loginForm.get('password');
}
@@ -47,11 +42,12 @@ export class LoginComponent extends BlankLayoutCardComponent implements OnInit {
if (this.loginForm.valid) {
this.authService.login(this.loginForm.getRawValue())
.subscribe(res => this.router.navigate(['/app/dashboard']),
- error => this.error = error.message);
+ error => this.error = error.message);
}
}
public onInputChange(event) {
event.target.required = true;
}
+
}
diff --git a/wg_dashboard_frontend/src/app/pages/user/user.module.ts b/wg_dashboard_frontend/src/app/pages/user/user.module.ts
new file mode 100644
index 0000000..cd2cf77
--- /dev/null
+++ b/wg_dashboard_frontend/src/app/pages/user/user.module.ts
@@ -0,0 +1,23 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { EditComponent } from './edit/edit.component';
+import {ReactiveFormsModule} from "@angular/forms";
+import {CardModule} from "../../../theme/components/card";
+import { LoginComponent } from './login/login.component';
+
+
+
+@NgModule({
+ declarations: [
+ EditComponent,
+ LoginComponent
+ ],
+ imports: [
+ CommonModule,
+ ReactiveFormsModule,
+ CardModule
+ ]
+})
+export class UserModule {
+
+}
diff --git a/wg_dashboard_frontend/src/app/services/auth/auth.guard.ts b/wg_dashboard_frontend/src/app/services/auth/auth.guard.ts
index 9d68626..d1e1898 100644
--- a/wg_dashboard_frontend/src/app/services/auth/auth.guard.ts
+++ b/wg_dashboard_frontend/src/app/services/auth/auth.guard.ts
@@ -16,7 +16,7 @@ export class AuthGuard implements CanActivate {
return true;
}
// Navigate to the login page with extras
- this.router.navigate(['/login']);
+ this.router.navigate(['/user/login']);
return false;
}
diff --git a/wg_dashboard_frontend/src/app/services/auth/auth.interceptor.ts b/wg_dashboard_frontend/src/app/services/auth/auth.interceptor.ts
index cca0f7a..2ce66ea 100644
--- a/wg_dashboard_frontend/src/app/services/auth/auth.interceptor.ts
+++ b/wg_dashboard_frontend/src/app/services/auth/auth.interceptor.ts
@@ -1,7 +1,8 @@
import { Injectable } from '@angular/core';
-import { Observable } from 'rxjs/index';
+import { Observable } from 'rxjs';
import {
+ HttpErrorResponse,
HttpEvent,
HttpHandler,
HttpInterceptor,
@@ -9,19 +10,31 @@ import {
} from '@angular/common/http';
import { AuthService } from './auth.service';
+import {tap} from "rxjs/operators";
+import {Router} from "@angular/router";
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
- constructor(private auth: AuthService) {
- }
+ constructor(private auth: AuthService, private router: Router) {}
public intercept(request: HttpRequest, next: HttpHandler): Observable> {
// add authorization token for full api requests
if (request.url.includes('api') && this.auth.isLoggedIn) {
request = request.clone({
- setHeaders: { Authorization: `Bearer ${this.auth.authToken}` },
+ setHeaders: { Authorization: `Bearer ${this.auth.user.access_token}`},
});
}
- return next.handle(request);
+
+ return next.handle(request).pipe( tap(() => {},
+ (err: any) => {
+ if (err instanceof HttpErrorResponse) {
+ if (err.status !== 401 && err.status !== 403) {
+ return;
+ }
+
+ this.auth.clearData();
+ this.router.navigate(['user/login']);
+ }
+ }));
}
}
diff --git a/wg_dashboard_frontend/src/app/services/auth/auth.service.ts b/wg_dashboard_frontend/src/app/services/auth/auth.service.ts
index 33e7981..c966c0f 100644
--- a/wg_dashboard_frontend/src/app/services/auth/auth.service.ts
+++ b/wg_dashboard_frontend/src/app/services/auth/auth.service.ts
@@ -4,6 +4,7 @@ import { BehaviorSubject, Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { environment } from '../../../environments/environment';
+import {User} from "../../interfaces/user";
const tokenName = 'token';
@@ -12,74 +13,65 @@ const tokenName = 'token';
})
export class AuthService {
- private isLogged$ = new BehaviorSubject(false);
- private url = `${environment.apiBaseUrl}/api/auth`;
- private user = { username: 'Luke', email: 'Luke@skywalker.com' }; // some data about user
+ public user: User = null;
+ private url = `${environment.apiBaseUrl}/api`;
- constructor(private http: HttpClient) {
-
- }
+ constructor(private http: HttpClient) {}
public get isLoggedIn(): boolean {
- return this.isLogged$.value;
+ return !!this.user?.access_token
}
public login(data): Observable {
- return this.http.post(`${this.url}/login`, data)
+ // Create form
+ let formData: FormData = new FormData();
+ formData.append('username', data.username);
+ formData.append('password', data.password);
+
+
+ return this.http.post(`${this.url}/login`, formData)
.pipe(
- map((res: { user: any, token: string }) => {
- this.user = res.user;
- localStorage.setItem(tokenName, res.token);
- // only for example
- localStorage.setItem('username', res.user.username);
- localStorage.setItem('email', res.user.email);
- this.isLogged$.next(true);
- return this.user;
+ map((res: any) => {
+ this._handleUser(res);
}));
}
+ public edit(formData: any){
+ return this.http.post(`${this.url}/user/edit`, formData)
+ .pipe(map((res: any) => {
+ this._handleUser(res);
+ }));
+ }
+
+ _handleUser(res: any){
+ const user: any = res.user;
+ user.access_token = res.access_token;
+ user.token_type = res.token_type;
+ localStorage.setItem("session", JSON.stringify(user));
+ this.init();
+ }
+
public logout() {
return this.http.get(`${this.url}/logout`)
.pipe(map((data) => {
- localStorage.clear();
- this.user = null;
- this.isLogged$.next(false);
+ this.clearData();
return of(false);
}));
}
- public signup(data) {
- return this.http.post(`${this.url}/signup`, data)
- .pipe(
- map((res: { user: any, token: string }) => {
- this.user = res.user;
- localStorage.setItem(tokenName, res.token);
- // only for example
- localStorage.setItem('username', res.user.username);
- localStorage.setItem('email', res.user.email);
- this.isLogged$.next(true);
- return this.user;
- }));
+
+ public clearData(){
+ this.user = null;
+ localStorage.clear();
+
}
public get authToken(): string {
return localStorage.getItem(tokenName);
}
- public get userData(): Observable {
- // send current user or load data from backend using token
- return this.loadUser();
- }
- private loadUser(): Observable {
- // use request to load user data with token
- // it's fake and useing only for example
- if (localStorage.getItem('username') && localStorage.getItem('email')) {
- this.user = {
- username: localStorage.getItem('username'),
- email: localStorage.getItem('email'),
- };
- }
- return of(this.user);
+ public init() {
+ this.user = JSON.parse(localStorage.getItem('session'));
}
}
diff --git a/wg_dashboard_frontend/src/app/services/auth/fakebackend.interceptor.ts b/wg_dashboard_frontend/src/app/services/auth/fakebackend.interceptor.ts
deleted file mode 100644
index 6e74ea1..0000000
--- a/wg_dashboard_frontend/src/app/services/auth/fakebackend.interceptor.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import {
- HttpEvent,
- HttpHandler,
- HttpInterceptor,
- HttpRequest,
- HttpResponse,
-} from '@angular/common/http';
-import { Injectable } from '@angular/core';
-import { Observable, of } from 'rxjs';
-import { delay, dematerialize, materialize, mergeMap } from 'rxjs/operators';
-
-@Injectable()
-export class FakeBackendInterceptor implements HttpInterceptor {
-
- private username = 'Luke';
- private email = 'Luke@skywalker.com';
-
- constructor() { }
-
- // with real backend you don't need it at all
- intercept(request: HttpRequest, next: HttpHandler): Observable> {
- return of(null).pipe(mergeMap(() => {
-
- // signup
- if (request.url.endsWith('/api/auth/signup') && request.method === 'POST') {
- const body = {
- token: 'token_' + this.makeID(),
- user: {
- username: request.body['username'],
- email: request.body['email'],
- },
- };
-
- return of(new HttpResponse({ body, status: 200 }));
- }
-
- // login
- if (request.url.endsWith('/api/auth/login') && request.method === 'POST') {
- const body = {
- token: 'token_' + this.makeID(),
- user: {
- username: this.username,
- email: request.body['email'],
- },
- };
-
- return of(new HttpResponse({ body, status: 200 }));
- }
-
- // logout
- if (request.url.endsWith('/api/auth/logout') && request.method === 'GET') {
- return of(new HttpResponse({ body: { success: true }, status: 200 }));
- }
-
- // at default just process the request
- return next.handle(request);
- }))
- .pipe(materialize())
- .pipe(delay(500))
- .pipe(dematerialize());
- }
-
- // generate random token
- private makeID(): string {
- let text = '';
- const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
- for (let i = 0; i < 25; i = i + 1) {
- text += possible.charAt(Math.floor(Math.random() * possible.length));
- }
- return text;
- }
-}
diff --git a/wg_dashboard_frontend/src/app/services/auth/index.ts b/wg_dashboard_frontend/src/app/services/auth/index.ts
index c0b0a71..cc02d1a 100644
--- a/wg_dashboard_frontend/src/app/services/auth/index.ts
+++ b/wg_dashboard_frontend/src/app/services/auth/index.ts
@@ -1,4 +1,3 @@
export { AuthGuard } from './auth.guard';
export { AuthService } from './auth.service';
export { AuthInterceptor } from './auth.interceptor';
-export { FakeBackendInterceptor } from './fakebackend.interceptor';
diff --git a/wg_dashboard_frontend/src/theme/components/sidebar/menu-link-item.component.ts b/wg_dashboard_frontend/src/theme/components/sidebar/menu-link-item.component.ts
index 116e65c..936cf96 100644
--- a/wg_dashboard_frontend/src/theme/components/sidebar/menu-link-item.component.ts
+++ b/wg_dashboard_frontend/src/theme/components/sidebar/menu-link-item.component.ts
@@ -30,7 +30,8 @@ export class MenuLinkItemComponent {
private navigate() {
const layout = (document.querySelector('.mdl-layout') as any).MaterialLayout;
- if (layout.drawer_.getAttribute('aria-hidden') !== 'true') {
+
+ if (layout.drawer_ && layout.drawer_.getAttribute('aria-hidden') !== 'true') {
layout.toggleDrawer();
}
}