Skip to content

Commit

Permalink
fix: add history entries
Browse files Browse the repository at this point in the history
Signed-off-by: Mirko Mollik <[email protected]>
  • Loading branch information
cre8 committed Apr 21, 2024
1 parent c3935f3 commit 88ec75f
Show file tree
Hide file tree
Showing 51 changed files with 741 additions and 124 deletions.
14 changes: 13 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
{
"files.eol": "\n",
"cSpell.words": ["keycloak", "sphereon"]
"cSpell.words": ["keycloak", "sphereon"],
"sqltools.connections": [
{
"previewLimit": 50,
"server": "localhost",
"port": 5432,
"driver": "PostgreSQL",
"database": "nestjs",
"username": "csalkfenvcda",
"password": "hwafkjhea",
"name": "Wallet-DB"
}
]
}
2 changes: 2 additions & 0 deletions apps/backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CredentialsModule } from './credentials/credentials.module';
import { DB_VALIDATION_SCHEMA, DbModule } from './db/db.module';
import { KeysModule } from './keys/keys.module';
import { Oid4vcModule } from './oid4vc/oid4vc.module';
import { HistoryModule } from './history/history.module';

@Module({
imports: [
Expand All @@ -20,6 +21,7 @@ import { Oid4vcModule } from './oid4vc/oid4vc.module';
KeysModule,
CredentialsModule,
Oid4vcModule,
HistoryModule,
],
})
export class AppModule {}
50 changes: 50 additions & 0 deletions apps/backend/src/history/entities/history.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ApiProperty } from '@nestjs/swagger';
import { Column, CreateDateColumn, Entity, PrimaryColumn } from 'typeorm';

type HistoryStatus = 'pending' | 'accepted' | 'declined';

@Entity()
export class History {
/**
* Unique ID of the history entry
*/
@PrimaryColumn({ primary: true })
id: string;

/**
* The user that owns the key
*/
@Column({ primary: true })
user: string;

/**
* Values
*/
@Column()
value: string;

/**
* Relying party
*/
@Column()
relyingParty: string;

/**
*
*/
@Column()
relyingPartyLogo: string;

/**
* Status of the history entry
*/
@ApiProperty({ type: 'string', enum: ['pending', 'accepted', 'declined'] })
@Column()
status: HistoryStatus;

/**
* Date of creation
*/
@CreateDateColumn()
created_at: Date;
}
18 changes: 18 additions & 0 deletions apps/backend/src/history/history.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { HistoryController } from './history.controller';

describe('HistoryController', () => {
let controller: HistoryController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [HistoryController],
}).compile();

controller = module.get<HistoryController>(HistoryController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});
});
25 changes: 25 additions & 0 deletions apps/backend/src/history/history.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Controller, Get, Param, UseGuards } from '@nestjs/common';
import { ApiOAuth2, ApiOperation, ApiTags } from '@nestjs/swagger';
import { AuthGuard, AuthenticatedUser } from 'nest-keycloak-connect';
import { HistoryService } from './history.service';
import { KeycloakUser } from 'src/auth/user';

@UseGuards(AuthGuard)
@ApiOAuth2([])
@ApiTags('history')
@Controller('history')
export class HistoryController {
constructor(private historyService: HistoryService) {}

@ApiOperation({ summary: 'get all elements' })
@Get()
all(@AuthenticatedUser() user: KeycloakUser) {
return this.historyService.all(user.sub);
}

@ApiOperation({ summary: 'get one element' })
@Get(':id')
getOne(@AuthenticatedUser() user: KeycloakUser, @Param('id') id: string) {
return this.historyService.getOne(id, user.sub);
}
}
13 changes: 13 additions & 0 deletions apps/backend/src/history/history.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { HistoryController } from './history.controller';
import { HistoryService } from './history.service';
import { History } from './entities/history.entity';

@Module({
imports: [TypeOrmModule.forFeature([History])],
controllers: [HistoryController],
providers: [HistoryService],
exports: [HistoryService],
})
export class HistoryModule {}
18 changes: 18 additions & 0 deletions apps/backend/src/history/history.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { HistoryService } from './history.service';

describe('HistoryService', () => {
let service: HistoryService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [HistoryService],
}).compile();

service = module.get<HistoryService>(HistoryService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
41 changes: 41 additions & 0 deletions apps/backend/src/history/history.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Injectable } from '@nestjs/common';
import { History } from './entities/history.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class HistoryService {
constructor(
@InjectRepository(History)
private historyRepository: Repository<History>
) {}

all(user: string) {
return this.historyRepository.find({ where: { user } });
}

getOne(id: string, user: string) {
return this.historyRepository.findOne({ where: { id, user } });
}

add(
session: string,
user: string,
relyingParty: string,
logo: string,
url: string
) {
const history = new History();
history.id = session;
history.user = user;
history.relyingParty = relyingParty;
history.relyingPartyLogo = logo;
history.value = url;
history.status = 'pending';
return this.historyRepository.save(history);
}

setStatus(id: string, status: 'accepted' | 'declined') {
return this.historyRepository.update({ id }, { status });
}
}
3 changes: 2 additions & 1 deletion apps/backend/src/oid4vc/oid4vc.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { Oid4vciController } from './oid4vci/oid4vci.controller';
import { Oid4vciService } from './oid4vci/oid4vci.service';
import { Oid4vpController } from './oid4vp/oid4vp.controller';
import { Oid4vpService } from './oid4vp/oid4vp.service';
import { HistoryModule } from 'src/history/history.module';

@Module({
imports: [HttpModule, KeysModule, CredentialsModule],
imports: [HttpModule, KeysModule, CredentialsModule, HistoryModule],
controllers: [Oid4vciController, Oid4vpController],
providers: [Oid4vciService, Oid4vpService],
})
Expand Down
28 changes: 17 additions & 11 deletions apps/backend/src/oid4vc/oid4vp/oid4vp.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import {
SupportedVersion,
VPTokenLocation,
type VerifiedAuthorizationRequest,
RPRegistrationMetadataPayload,
} from '@sphereon/did-auth-siop';
import { SdJwtDecodedVerifiableCredentialWithKbJwtInput } from '@sphereon/pex';
import { CredentialsService } from 'src/credentials/credentials.service';
import { KeysService } from 'src/keys/keys.service';
import { v4 as uuid } from 'uuid';
import { Oid4vpParseRepsonse } from './dto/parse-response.dto';
import { SubmissionRequest } from './dto/submission-request.dto';
import { HistoryService } from 'src/history/history.service';

interface Session {
user: string;
Expand All @@ -34,15 +36,12 @@ export class Oid4vpService {

constructor(
private credentialsService: CredentialsService,
private keysService: KeysService
private keysService: KeysService,
private historyService: HistoryService
) {
this.sdjwt = new SDJwtVcInstance({ hasher: digest });
}

process(url: string, user: string) {
throw new Error('Method not implemented.');
}

async parse(url: string, user: string): Promise<Oid4vpParseRepsonse> {
const sessionId = uuid();
const op = await this.getOp(user);
Expand All @@ -53,6 +52,13 @@ export class Oid4vpService {
await op.verifyAuthorizationRequest(
parsedAuthReqURI.requestObjectJwt as string
);
const issuer =
(
verifiedAuthReqWithJWT.authorizationRequestPayload
.client_metadata as RPRegistrationMetadataPayload
).client_name ?? verifiedAuthReqWithJWT.issuer;
const logo = verifiedAuthReqWithJWT.registrationMetadataPayload.logo_uri;
await this.historyService.add(sessionId, user, issuer, logo, url);

// get all credentials from the client, required for the presentation exchange
const credentials = (await this.credentialsService.findAll(user)).map(
Expand Down Expand Up @@ -113,8 +119,7 @@ export class Oid4vpService {
return {
rp: {
name: verifiedAuthReqWithJWT.registrationMetadataPayload.client_name,
logo: verifiedAuthReqWithJWT.registrationMetadataPayload
.client_logo_uri,
logo: verifiedAuthReqWithJWT.registrationMetadataPayload.logo_uri,
},
purpose: pds[0].definition.purpose,
requests,
Expand Down Expand Up @@ -144,7 +149,8 @@ export class Oid4vpService {
).kbJwt;
args.selectedCredentials[0];
//TODO: set the correct value for aud
const aud = 'Audience';
const aud =
session.verifiedAuthReqWithJWT.authorizationRequest.payload.client_id;
const cnf = args.presentation.decodedPayload.cnf;
const kid = this.keysService.decodeDidJWK(cnf.kid).kid as string;
const signwedKbJwt = await this.keysService.signkbJwt(
Expand Down Expand Up @@ -194,22 +200,22 @@ export class Oid4vpService {
const res = await session.op
.submitAuthorizationResponse(authenticationResponseWithJWT)
.catch(() => '');
console.log(res);
await this.historyService.setStatus(sessionId, 'accepted');
this.sessions.delete(sessionId);
//TODO: save that the user accepted the request and the data was sent successfully
}

/**
* Deletes the session of a user since he declined the request
* @param id
* @param user
*/
decline(id: string, user: string) {
async decline(id: string, user: string) {
//TODO: document that the user declined it
const session = this.sessions.get(id);
if (!session || session.user !== user) {
throw new ConflictException('Session not found');
}
await this.historyService.setStatus(id, 'declined');
this.sessions.delete(id);
}

Expand Down
5 changes: 0 additions & 5 deletions apps/holder/projects/browser-extension/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Routes } from '@angular/router';
import { CredentialsListComponent } from '../../../shared/credentials/credentials-list/credentials-list.component';
import { CredentialsShowComponent } from '../../../shared/credentials/credentials-show/credentials-show.component';
import { KeysListComponent } from '../../../shared/keys-list/keys-list.component';
import { ScannerComponent } from './scanner/scanner.component';
import { SettingsComponent } from '../../../shared/settings/settings.component';
import { authGuard } from './auth/auth.guard';
Expand All @@ -20,10 +19,6 @@ export const routes: Routes = [
path: 'scan',
component: ScannerComponent,
},
{
path: 'keys',
component: KeysListComponent,
},
{
path: 'credentials',
component: CredentialsListComponent,
Expand Down
6 changes: 6 additions & 0 deletions apps/holder/projects/pwa/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
>
<span class="info">credentials</span>
</div>
<div fxLayout="column" fxLayoutAlign=" center">
<a mat-icon-button routerLink="/history" routerLinkActive="active-link"
><mat-icon>history</mat-icon></a
>
<span class="info">History</span>
</div>
<div fxLayout="column" fxLayoutAlign=" center">
<a mat-icon-button routerLink="/settings" routerLinkActive="active-link"
><mat-icon>settings</mat-icon></a
Expand Down
15 changes: 10 additions & 5 deletions apps/holder/projects/pwa/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { Routes } from '@angular/router';
import { authGuard } from '../../../shared/auth/auth.guard';
import { CredentialsListComponent } from '../../../shared/credentials/credentials-list/credentials-list.component';
import { CredentialsShowComponent } from '../../../shared/credentials/credentials-show/credentials-show.component';
import { KeysListComponent } from '../../../shared/keys-list/keys-list.component';
import { SettingsComponent } from '../../../shared/settings/settings.component';
import { ScannerComponent } from './scanner/scanner.component';
import { LoginComponent } from '../../../shared/login/login.component';
import { HistoryListComponent } from '../../../shared/history/history-list/history-list.component';
import { HistoryShowComponent } from '../../../shared/history/history-show/history-show.component';

export const routes: Routes = [
{
Expand All @@ -21,10 +22,6 @@ export const routes: Routes = [
path: 'scan',
component: ScannerComponent,
},
{
path: 'keys',
component: KeysListComponent,
},
{
path: 'credentials',
component: CredentialsListComponent,
Expand All @@ -33,6 +30,14 @@ export const routes: Routes = [
path: 'credentials/:id',
component: CredentialsShowComponent,
},
{
path: 'history',
component: HistoryListComponent,
},
{
path: 'history/:id',
component: HistoryShowComponent,
},
{
path: 'settings',
component: SettingsComponent,
Expand Down
Binary file not shown.
Binary file modified apps/holder/projects/pwa/src/assets/icons/icon-128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/holder/projects/pwa/src/assets/icons/icon-144x144.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/holder/projects/pwa/src/assets/icons/icon-152x152.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file modified apps/holder/projects/pwa/src/assets/icons/icon-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/holder/projects/pwa/src/assets/icons/icon-72x72.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/holder/projects/pwa/src/assets/icons/icon-96x96.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 88ec75f

Please sign in to comment.