get full name information, make sure its displayed in the UI.
added a white on transparent banner image.
This commit is contained in:
parent
c0e996dc74
commit
bcb3f58d6e
|
@ -79,12 +79,13 @@ cd frontend
|
|||
npm run dist -- -c sandbox
|
||||
|
||||
# In terminal #2, run the following
|
||||
docker build -t fasten-couchdb -f docker/couchdb/Dockerfile .
|
||||
docker run --rm -it -p 5984:5984 -v `pwd`/.couchdb/data:/opt/couchdb/data -v `pwd`/.couchdb/config:/opt/couchdb/etc/local.d fasten-couchdb
|
||||
|
||||
# In terminal #3, run the following
|
||||
go mod vendor
|
||||
go run backend/cmd/fasten/fasten.go start --config ./config.dev.yaml --debug
|
||||
|
||||
# In terminal #3, run the following
|
||||
docker build -t fasten-couchdb -f docker/couchdb/Dockerfile .
|
||||
docker run --rm -it -p 5984:5984 -v `pwd`/.couchdb/data:/opt/couchdb/data -v `pwd`/.couchdb/config:/opt/couchdb/etc/local.d fasten-couchdb
|
||||
```
|
||||
|
||||
Now you can open a browser to `http://localhost:9090` to see the Fasten UI.
|
||||
|
|
|
@ -57,6 +57,8 @@ type couchDbUser struct {
|
|||
Type string `json:"type"`
|
||||
Roles []string `json:"roles"`
|
||||
Password string `json:"password"`
|
||||
|
||||
FullName string `json:"full_name"`
|
||||
}
|
||||
|
||||
func (cr *couchdbRepository) CreateUser(ctx context.Context, user *models.User) error {
|
||||
|
@ -67,6 +69,8 @@ func (cr *couchdbRepository) CreateUser(ctx context.Context, user *models.User)
|
|||
Type: "user",
|
||||
Roles: []string{},
|
||||
Password: user.Password,
|
||||
|
||||
FullName: user.FullName,
|
||||
}
|
||||
db := cr.client.DB(ctx, "_users")
|
||||
_, err := db.Put(ctx, newUser.ID, newUser)
|
||||
|
@ -97,6 +101,19 @@ func (cr *couchdbRepository) VerifyUser(ctx context.Context, user *models.User)
|
|||
}
|
||||
log.Printf("SESSION INFO: %v", session)
|
||||
//TODO: return session info
|
||||
|
||||
//lookup the user in the user database using admin creds
|
||||
adminDb := cr.client.DB(ctx, "_users")
|
||||
userRow := adminDb.Get(ctx, kivik.UserPrefix+session.Name)
|
||||
userDoc := map[string]interface{}{}
|
||||
err = userRow.ScanDoc(&userDoc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if userFullName, hasUserFullName := userDoc["full_name"]; hasUserFullName {
|
||||
user.FullName = userFullName.(string)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package models
|
||||
|
||||
type User struct {
|
||||
FullName string `json:"full_name"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
func AuthSignup(c *gin.Context) {
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
appConfig := c.MustGet("CONFIG").(config.Interface)
|
||||
|
||||
var user models.User
|
||||
if err := c.ShouldBindJSON(&user); err != nil {
|
||||
|
@ -25,7 +26,10 @@ func AuthSignup(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
//TODO: we can derive the encryption key and the hash'ed user from the responseData sub. For now the Sub will be the user id prepended with hello.
|
||||
userFastenToken, err := jwtGenerateFastenTokenFromUser(user, appConfig.GetString("jwt.issuer.key"))
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": userFastenToken})
|
||||
}
|
||||
|
||||
func AuthSignin(c *gin.Context) {
|
||||
|
@ -49,14 +53,22 @@ func AuthSignin(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, gin.H{"success": true, "data": userFastenToken})
|
||||
}
|
||||
|
||||
type UserRegisteredClaims struct {
|
||||
FullName string `json:"full_name"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func jwtGenerateFastenTokenFromUser(user models.User, issuerSigningKey string) (string, error) {
|
||||
log.Printf("ISSUER KEY: " + issuerSigningKey)
|
||||
userClaims := jwt.RegisteredClaims{
|
||||
userClaims := UserRegisteredClaims{
|
||||
FullName: user.FullName,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
// In JWT, the expiry time is expressed as unix milliseconds
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: "docker-fastenhealth",
|
||||
Subject: user.Username,
|
||||
},
|
||||
}
|
||||
|
||||
//FASTEN_JWT_ISSUER_KEY
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<div class="az-img-user">
|
||||
<img src="assets/logo/logo-text.png" alt="">
|
||||
</div><!-- az-img-user -->
|
||||
<h6>{{current_user}}</h6>
|
||||
<h6>{{current_user_claims.full_name || current_user_claims.email || current_user_claims.sub}}</h6>
|
||||
<span>Adminstrator</span>
|
||||
</div><!-- az-header-profile -->
|
||||
|
||||
|
|
|
@ -2,20 +2,21 @@ import { Component, OnInit } from '@angular/core';
|
|||
import {FastenDbService} from '../../services/fasten-db.service';
|
||||
import { Router } from '@angular/router';
|
||||
import {AuthService} from '../../services/auth.service';
|
||||
import {UserRegisteredClaims} from '../../models/fasten/user-registered-claims';
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.scss']
|
||||
})
|
||||
export class HeaderComponent implements OnInit {
|
||||
current_user: string
|
||||
current_user_claims: UserRegisteredClaims
|
||||
constructor(private authService: AuthService, private router: Router) { }
|
||||
|
||||
ngOnInit() {
|
||||
try {
|
||||
this.current_user = this.authService.GetCurrentUser()
|
||||
this.current_user_claims = this.authService.GetCurrentUser()
|
||||
} catch(e){
|
||||
this.current_user = "unknown"
|
||||
this.current_user_claims = new UserRegisteredClaims()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
export class UserRegisteredClaims {
|
||||
iss: string //Issuer
|
||||
sub: string //Subject
|
||||
aud?: string //Audience
|
||||
exp: number //ExpiresAt (unix timestamp)
|
||||
nbf?: number //NotBefore (unix timestamp)
|
||||
iat?: number //IssuedAt (unix timestamp)
|
||||
id?: string //ID
|
||||
|
||||
full_name: string //FullName
|
||||
picture: string //Picture
|
||||
email: string //Email
|
||||
}
|
|
@ -40,6 +40,7 @@ export class AuthSigninComponent implements OnInit {
|
|||
this.authService.IdpCallback(idpType, state, code)
|
||||
.then(() => this.router.navigateByUrl('/dashboard'))
|
||||
.catch((err)=>{
|
||||
console.error("idpCallback error:", err)
|
||||
const toastNotification = new ToastNotification()
|
||||
toastNotification.type = ToastType.Error
|
||||
toastNotification.message = "an error occurred while signing in"
|
||||
|
|
|
@ -20,6 +20,20 @@
|
|||
|
||||
<form (ngSubmit)="signupSubmit()" #userForm="ngForm">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Firstname & Lastname</label>
|
||||
<input [(ngModel)]="newUser.full_name" name="full_name" #full_name="ngModel" required minlength="2" type="text" class="form-control" placeholder="Enter your firstname and lastname">
|
||||
|
||||
<div *ngIf="full_name.invalid && (full_name.dirty || full_name.touched)" class="alert alert-danger">
|
||||
<div *ngIf="full_name.errors?.['required']">
|
||||
Name is required.
|
||||
</div>
|
||||
<div *ngIf="full_name.errors?.['minlength']">
|
||||
Name must be at least 4 characters long.
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- form-group -->
|
||||
|
||||
<div class="form-group">
|
||||
<label>Username</label>
|
||||
<input [(ngModel)]="newUser.username" name="username" #username="ngModel" required minlength="5" type="text" class="form-control" placeholder="Enter your username">
|
||||
|
|
|
@ -9,6 +9,7 @@ import * as Oauth from '@panva/oauth4webapi';
|
|||
import {SourceState} from '../models/fasten/source-state';
|
||||
import {Session} from '../models/database/session';
|
||||
import * as jose from 'jose';
|
||||
import {UserRegisteredClaims} from '../models/fasten/user-registered-claims';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
@ -161,7 +162,7 @@ export class AuthService {
|
|||
return localStorage.getItem(this.FASTEN_JWT_LOCALSTORAGE_KEY);
|
||||
}
|
||||
|
||||
public GetCurrentUser(): string {
|
||||
public GetCurrentUser(): UserRegisteredClaims {
|
||||
let authToken = this.GetAuthToken()
|
||||
if(!authToken){
|
||||
throw new Error("no auth token found")
|
||||
|
@ -169,7 +170,8 @@ export class AuthService {
|
|||
|
||||
//parse the authToken to get user information
|
||||
let jwtClaims = jose.decodeJwt(authToken)
|
||||
return jwtClaims.sub
|
||||
// @ts-ignore
|
||||
return jwtClaims as UserRegisteredClaims
|
||||
}
|
||||
|
||||
public async Logout(): Promise<any> {
|
||||
|
|
|
@ -63,7 +63,7 @@ export class FastenDbService extends PouchdbRepository {
|
|||
}
|
||||
|
||||
//parse the authToken to get user information
|
||||
this.current_user = this.authService.GetCurrentUser()
|
||||
this.current_user = this.authService.GetCurrentUser().sub
|
||||
|
||||
// add JWT bearer token header to all requests
|
||||
// https://stackoverflow.com/questions/62129654/how-to-handle-jwt-authentication-with-rxdb
|
||||
|
|
|
@ -20,7 +20,7 @@ export class QueueService {
|
|||
if (typeof Worker !== 'undefined') {
|
||||
const sourceSync = new SourceSyncMessage()
|
||||
sourceSync.source = source
|
||||
sourceSync.current_user = this.authService.GetCurrentUser()
|
||||
sourceSync.current_user = this.authService.GetCurrentUser().sub
|
||||
sourceSync.auth_token = this.authService.GetAuthToken()
|
||||
sourceSync.couchdb_endpoint_base = environment.couchdb_endpoint_base
|
||||
sourceSync.fasten_api_endpoint_base = environment.fasten_api_endpoint_base
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
@ -1,4 +1,5 @@
|
|||
export class User {
|
||||
full_name?: string
|
||||
username?: string
|
||||
password?: string
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue