Commit dd2a3ea7 authored by Hidde-Jan Jongsma's avatar Hidde-Jan Jongsma

Implement type definition

CredentialType are organization specific and point to a JolocomCredentialType and a irma credential (both optional).
parent 0c1bcd3b
import {
IsNotEmpty,
IsString,
IsNotEmptyObject,
IsArray,
} from 'class-validator';
import { BaseMetadata } from 'cred-types-jolocom-core/js/types';
export class CreateJolocomTypeDto {
@IsString()
@IsNotEmpty()
type: string;
@IsString()
@IsNotEmpty()
name: string;
@IsArray()
context: BaseMetadata['context'];
@IsNotEmptyObject()
claimInterface: BaseMetadata['claimInterface'];
}
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import {
BaseEntity,
Entity,
PrimaryGeneratedColumn,
Column,
OneToMany,
} from 'typeorm';
import { BaseMetadata } from 'cred-types-jolocom-core/js/types';
import { CredentialType } from 'src/types/credential-type.entity';
@Entity()
export class JolocomCredentialType extends BaseEntity {
......@@ -17,4 +24,10 @@ export class JolocomCredentialType extends BaseEntity {
@Column('simple-json')
claimInterface: BaseMetadata['claimInterface'];
@OneToMany(
() => CredentialType,
type => type.organization,
)
credentialTypes: CredentialType[];
}
import { Test, TestingModule } from '@nestjs/testing';
import { JolocomController } from './jolocom.controller';
describe('Jolocom Controller', () => {
let controller: JolocomController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [JolocomController],
}).compile();
controller = module.get<JolocomController>(JolocomController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
import { Controller, Get, Post, Body } from '@nestjs/common';
import { JolocomService } from './jolocom.service';
import { CreateJolocomTypeDto } from './create-jolocom-type.dto';
@Controller('connectors/jolocom')
export class JolocomController {
constructor(private jolocomService: JolocomService) {}
@Get()
index() {
return this.jolocomService.findAllTypes();
}
@Post()
create(@Body() jolocomTypeData: CreateJolocomTypeDto) {
return this.jolocomService.createType(jolocomTypeData);
}
}
......@@ -4,10 +4,12 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { JolocomService } from './jolocom.service';
import { JolocomWallet } from './jolocom-wallet.entity';
import { JolocomCredentialType } from './jolocom-credential-type.entity';
import { JolocomController } from './jolocom.controller';
@Module({
imports: [TypeOrmModule.forFeature([JolocomWallet, JolocomCredentialType])],
providers: [JolocomService],
exports: [JolocomService],
exports: [JolocomService, TypeOrmModule],
controllers: [JolocomController],
})
export class JolocomModule {}
......@@ -18,7 +18,9 @@ export class JolocomService implements ConnectorService {
constructor(
@InjectRepository(JolocomWallet)
private walletRepository: Repository<JolocomWallet>,
private walletsRepository: Repository<JolocomWallet>,
@InjectRepository(JolocomCredentialType)
private typesRepository: Repository<JolocomCredentialType>,
) {
this.registry = JolocomLib.registries.jolocom.create();
this.logger = new Logger(JolocomService.name);
......@@ -34,6 +36,20 @@ export class JolocomService implements ConnectorService {
/* JolocomService specific */
async findAllTypes() {
return this.typesRepository.find();
}
async createType(typeData) {
const type = new JolocomCredentialType();
type.type = typeData.type;
type.name = typeData.name;
type.claimInterface = typeData.claimInterface;
type.context = typeData.context;
return this.typesRepository.save(type);
}
async createWalletForOrganization(organization: Organization) {
this.logger.debug(`Creating wallet for ${organization.name}`);
const seed = JolocomWallet.randomSeed();
......@@ -45,7 +61,7 @@ export class JolocomService implements ConnectorService {
wallet.password = password;
wallet.encryptedSeedHex = keyProvider.encryptedSeed; // Already in hex format
await this.walletRepository.save(wallet);
await this.walletsRepository.save(wallet);
this.logger.debug(`Created wallet for ${organization.name}`);
return wallet;
}
......
......@@ -10,6 +10,7 @@ import { randomBytes } from 'crypto';
import { JolocomWallet } from '../connectors/jolocom/jolocom-wallet.entity';
import { CredentialVerifyRequest } from 'src/requests/credential-verify-request.entity';
import { CredentialIssueRequest } from 'src/requests/credential-issue-request.entity';
import { CredentialType } from 'src/types/credential-type.entity';
const JWT_SECRET_BITS = 32;
......@@ -34,6 +35,12 @@ export class Organization {
)
jolocomWallet: JolocomWallet;
@OneToMany(
() => CredentialType,
type => type.organization,
)
credentialTypes: CredentialType[];
@OneToMany(
() => CredentialVerifyRequest,
request => request.requestor,
......
......@@ -10,6 +10,6 @@ import { ConnectorsModule } from 'src/connectors/connectors.module';
imports: [TypeOrmModule.forFeature([Organization]), ConnectorsModule],
controllers: [OrganizationsController],
providers: [OrganizationsService],
exports: [OrganizationsService],
exports: [OrganizationsService, TypeOrmModule],
})
export class OrganizationsModule {}
......@@ -7,6 +7,7 @@ import {
} from 'typeorm';
import { CredentialRequest } from './credential-request.interface';
import { Organization } from '../organizations/organization.entity';
import { CredentialType } from 'src/types/credential-type.entity';
interface CredentialData {
[key: string]: string | number | boolean | null;
......@@ -27,8 +28,11 @@ export class CredentialIssueRequest implements CredentialRequest {
@Column()
callbackUrl: string;
@Column()
type: string;
@ManyToOne(
() => CredentialType,
type => type.issueRequests,
)
type: CredentialType;
@Column()
@Generated('uuid')
......
import { Organization } from '../organizations/organization.entity';
import { CredentialType } from 'src/types/credential-type.entity';
export interface CredentialRequest {
requestId: string;
iss: string;
type: string;
type: CredentialType;
callbackUrl: string;
requestor: Organization;
}
......@@ -7,6 +7,7 @@ import {
} from 'typeorm';
import { CredentialRequest } from './credential-request.interface';
import { Organization } from '../organizations/organization.entity';
import { CredentialType } from 'src/types/credential-type.entity';
export interface CredentialVerifyRequestData {
iss: string;
......@@ -22,8 +23,11 @@ export class CredentialVerifyRequest implements CredentialRequest {
@Column()
callbackUrl: string;
@Column()
type: string;
@ManyToOne(
() => CredentialType,
type => type.verifyRequests,
)
type: CredentialType;
@Column()
@Generated('uuid')
......
import { Module } from '@nestjs/common';
import { RequestsService } from './requests.service';
import { OrganizationsModule } from '../organizations/organizations.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { OrganizationsModule } from '../organizations/organizations.module';
import { TypesModule } from 'src/types/types.module';
import { CredentialIssueRequest } from './credential-issue-request.entity';
import { CredentialVerifyRequest } from './credential-verify-request.entity';
import { RequestsService } from './requests.service';
import { GetIssueRequestPipe, GetVerifyRequestPipe } from './get-request.pipe';
@Module({
imports: [
TypeOrmModule.forFeature([CredentialIssueRequest, CredentialVerifyRequest]),
OrganizationsModule,
TypesModule,
],
providers: [RequestsService, GetIssueRequestPipe, GetVerifyRequestPipe],
exports: [RequestsService, GetIssueRequestPipe, GetVerifyRequestPipe],
......
......@@ -13,6 +13,7 @@ import {
} from './credential-issue-request.entity';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { CredentialType } from 'src/types/credential-type.entity';
export class InvalidRequestJWT extends Error {}
......@@ -23,21 +24,24 @@ export class RequestsService {
logger: Logger;
constructor(
private organizationsService: OrganizationsService,
@InjectRepository(Organization)
private organizationsRepository: Repository<Organization>,
@InjectRepository(CredentialIssueRequest)
private issueRequestRepository: Repository<CredentialIssueRequest>,
private issueRequestsRepository: Repository<CredentialIssueRequest>,
@InjectRepository(CredentialVerifyRequest)
private verifyRequestRepository: Repository<CredentialVerifyRequest>,
private verifyRequestsRepository: Repository<CredentialVerifyRequest>,
@InjectRepository(CredentialType)
private typesRepository: Repository<CredentialType>,
) {
this.logger = new Logger(RequestsService.name);
}
async findVerifyRequestByIdentifier(uuid: string) {
return this.verifyRequestRepository.findOne({ uuid });
return this.verifyRequestsRepository.findOne({ uuid });
}
async findIssueRequestByIdentifier(uuid: string) {
return this.issueRequestRepository.findOne({ uuid });
return this.issueRequestsRepository.findOne({ uuid });
}
async findVerifyRequestByRequestId(requestId: string) {
......@@ -83,14 +87,18 @@ export class RequestsService {
CredentialVerifyRequestData
>(jwt);
const type = await this.typesRepository.findOneOrFail({
organization: requestor,
type: request.type,
});
const verifyRequest = new CredentialVerifyRequest();
verifyRequest.requestor = requestor;
verifyRequest.type = request.type;
verifyRequest.type = type;
verifyRequest.callbackUrl = request.callbackUrl;
await this.verifyRequestRepository.save(verifyRequest);
return verifyRequest;
return this.verifyRequestsRepository.save(verifyRequest);
}
async decodeIssueRequestToken(jwt: string) {
......@@ -98,15 +106,19 @@ export class RequestsService {
CredentialIssueRequestData
>(jwt);
const type = await this.typesRepository.findOneOrFail({
organization: requestor,
type: request.type,
});
const issueRequest = new CredentialIssueRequest();
issueRequest.requestor = requestor;
issueRequest.type = request.type;
issueRequest.type = type;
issueRequest.callbackUrl = request.callbackUrl;
issueRequest.data = request.data;
await this.issueRequestRepository.save(issueRequest);
return issueRequest;
return this.issueRequestsRepository.save(issueRequest);
}
async decodeAndVerifyJwt<T = unknown>(
......@@ -123,13 +135,9 @@ export class RequestsService {
);
}
const requestor = await this.organizationsService.findByIdentifier(
decoded.iss,
);
if (!requestor) {
throw new Error(`Could not find requestor from: ${decoded.iss}`);
}
const requestor = await this.organizationsRepository.findOneOrFail({
uuid: decoded.iss,
});
// Verify that jwt is signed by specified issuer
const request = verify(jwt, requestor.sharedSecret, {
......
import { IsNotEmpty, IsString, IsOptional, IsNumber } from 'class-validator';
export class CreateTypeDto {
@IsString()
@IsNotEmpty()
type: string;
@IsOptional()
@IsString()
irmaType: string;
@IsNumber()
jolocomCredentialTypeId: number;
@IsNumber()
organizationId: number;
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
OneToMany,
} from 'typeorm';
import { Organization } from '../organizations/organization.entity';
import { JolocomCredentialType } from 'src/connectors/jolocom/jolocom-credential-type.entity';
import { CredentialVerifyRequest } from 'src/requests/credential-verify-request.entity';
import { CredentialIssueRequest } from 'src/requests/credential-issue-request.entity';
@Entity()
export class CredentialType {
@PrimaryGeneratedColumn()
id: number;
@Column()
type: string;
@ManyToOne(
() => Organization,
organization => organization.credentialTypes,
)
organization: Organization;
@ManyToOne(
() => JolocomCredentialType,
credentialType => credentialType.credentialTypes,
{
nullable: true,
},
)
jolocomType: JolocomCredentialType;
@Column({ nullable: true })
irmaType: string;
@OneToMany(
() => CredentialVerifyRequest,
request => request.type,
)
verifyRequests: CredentialVerifyRequest[];
@OneToMany(
() => CredentialIssueRequest,
request => request.type,
)
issueRequests: CredentialIssueRequest[];
}
import { Test, TestingModule } from '@nestjs/testing';
import { TypesController } from './types.controller';
describe('Types Controller', () => {
let controller: TypesController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [TypesController],
}).compile();
controller = module.get<TypesController>(TypesController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
import { Controller, Post, Body, Get } from '@nestjs/common';
import { TypesService } from './types.service';
import { CreateTypeDto } from './create-type.dto';
@Controller('types')
export class TypesController {
constructor(private typesService: TypesService) {}
@Get()
index() {
return this.typesService.findAll();
}
@Post()
create(@Body() typeData: CreateTypeDto) {
return this.typesService.create(typeData);
}
}
import { Module } from '@nestjs/common';
import { TypesController } from './types.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CredentialType } from './credential-type.entity';
import { TypesService } from './types.service';
import { OrganizationsModule } from 'src/organizations/organizations.module';
import { JolocomModule } from 'src/connectors/jolocom/jolocom.module';
@Module({
imports: [
TypeOrmModule.forFeature([CredentialType]),
OrganizationsModule,
JolocomModule,
],
controllers: [TypesController],
providers: [TypesService],
exports: [TypeOrmModule],
})
export class TypesModule {}
import { Test, TestingModule } from '@nestjs/testing';
import { TypesService } from './types.service';
describe('TypesService', () => {
let service: TypesService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [TypesService],
}).compile();
service = module.get<TypesService>(TypesService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { CredentialType } from './credential-type.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Organization } from 'src/organizations/organization.entity';
import { JolocomCredentialType } from 'src/connectors/jolocom/jolocom-credential-type.entity';
interface CreateData {
organizationId: number | string;
jolocomCredentialTypeId: number | string;
irmaType: string;
type: string;
}
@Injectable()
export class TypesService {
constructor(
@InjectRepository(CredentialType)
private typesRespository: Repository<CredentialType>,
@InjectRepository(Organization)
private organizationsRepository: Repository<Organization>,
@InjectRepository(JolocomCredentialType)
private jolocomTypeRepository: Repository<JolocomCredentialType>,
) {}
async findAll() {
return this.typesRespository.find({
relations: ['organization', 'jolocomType'],
});
}
async create({
organizationId,
jolocomCredentialTypeId,
irmaType,
type,
}: CreateData) {
const organization = await this.organizationsRepository.findOneOrFail(
organizationId,
);
const jolocomCredentialType = await this.jolocomTypeRepository.findOneOrFail(
jolocomCredentialTypeId,
);
const credentialType = new CredentialType();
credentialType.type = type;
credentialType.irmaType = irmaType;
credentialType.organization = organization;
credentialType.jolocomType = jolocomCredentialType;
return this.typesRespository.save(credentialType);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment