Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/persist installed certificate hashes #284

Merged
merged 4 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 00_Base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,4 @@ export { assert, notNull, deepDirectionalEqual } from './assertion/assertion';
export { UnauthorizedError } from './interfaces/api/exception/UnauthorizedError';
export { AuthorizationSecurity } from './interfaces/api/AuthorizationSecurity';
export { Ajv };
export declare type Constructable<T> = new (...args: any[]) => T;
10 changes: 10 additions & 0 deletions 00_Base/src/interfaces/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ export abstract class CrudRepository<T> extends EventEmitter {
return result;
}

public async bulkCreate(
values: T[],
clazz: any, // todo: should Model be in base so it can be used here?
): Promise<T[]> {
const result = await this._bulkCreate(values, clazz);
this.emit('created', result);
return result;
}

/**
* Creates a new entry in the database with the specified value and key.
* If a namespace is provided, the entry will be created within that namespace.
Expand Down Expand Up @@ -250,6 +259,7 @@ export abstract class CrudRepository<T> extends EventEmitter {
abstract existByQuery(query: object, namespace?: string): Promise<number>;

protected abstract _create(value: T, namespace?: string): Promise<T>;
protected abstract _bulkCreate(value: T[], namespace?: string): Promise<T[]>;

protected abstract _createByKey(
value: T,
Expand Down
1 change: 1 addition & 0 deletions 00_Base/src/ocpp/persistence/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum Namespace {
AuthorizationRestrictions = 'AuthorizationRestrictions',
BootConfig = 'Boot',
Certificate = 'Certificate',
InstalledCertificate = 'InstalledCertificate',
CertificateChain = 'CertificateChain',
ChargingNeeds = 'ChargingNeeds',
ChargingProfile = 'ChargingProfile',
Expand Down
3 changes: 3 additions & 0 deletions 01_Data/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// SPDX-License-Identifier: Apache 2.0

import { Transaction as SequelizeTransaction } from 'sequelize';

export { SequelizeTransaction };
export { IdTokenAdditionalInfo } from './layers/sequelize/model/Authorization/IdTokenAdditionalInfo';
export * as sequelize from './layers/sequelize';
Expand Down Expand Up @@ -31,6 +32,7 @@ export {
VariableCharacteristics,
VariableStatus,
Certificate,
InstalledCertificate,
CountryNameEnumType,
TransactionEvent,
IdToken,
Expand All @@ -46,6 +48,7 @@ export {
SequelizeBootRepository,
SequelizeCallMessageRepository,
SequelizeCertificateRepository,
SequelizeInstalledCertificateRepository,
SequelizeChargingProfileRepository,
SequelizeChargingStationSecurityInfoRepository,
SequelizeDeviceModelRepository,
Expand Down
62 changes: 42 additions & 20 deletions 01_Data/src/interfaces/repositories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,74 @@
// SPDX-License-Identifier: Apache 2.0

import {
MessageInfoType,
type AuthorizationData,
type BootConfig,
type CallAction,
ChargingLimitSourceEnumType,
ChargingProfilePurposeEnumType,
ChargingProfileType,
type ChargingStateEnumType,
ChargingStationSequenceType,
type ComponentType,
CompositeScheduleType,
type CrudRepository,
type EventDataType,
type EVSEType,
type GetVariableResultType,
type CrudRepository,
type IdTokenType,
MessageInfoType,
MeterValueType,
type MonitoringDataType,
NotifyEVChargingNeedsRequest,
type RegistrationStatusEnumType,
type ReportDataType,
ReserveNowRequest,
type SecurityEventNotificationRequest,
type SetMonitoringDataType,
type SetMonitoringResultType,
type SetVariableDataType,
type SetVariableResultType,
type StatusInfoType,
StatusNotificationRequest,
type TransactionEventRequest,
UpdateEnumType,
type VariableAttributeType,
type VariableMonitoringType,
type VariableType,
ChargingProfileType,
ChargingProfilePurposeEnumType,
NotifyEVChargingNeedsRequest,
ChargingLimitSourceEnumType,
CompositeScheduleType,
StatusNotificationRequest,
ReserveNowRequest,
MeterValueType,
UpdateEnumType,
ChargingStationSequenceType,
} from '@citrineos/base';
import { type AuthorizationQuerystring } from './queries/Authorization';
import { CallMessage, ChargingStationSecurityInfo, ChargingStationSequence, CompositeSchedule, MeterValue, type Transaction, VariableCharacteristics } from '../layers/sequelize';
import { type VariableAttribute } from '../layers/sequelize';
import {
type Authorization,
type Boot,
CallMessage,
type Certificate,
ChargingNeeds,
ChargingProfile,
type ChargingStation,
ChargingStationSecurityInfo,
ChargingStationSequence,
type Component,
CompositeSchedule,
type EventData,
Evse,
type Location,
MessageInfo,
MeterValue,
Reservation,
type SecurityEvent,
Subscription,
Tariff,
type Transaction,
type Variable,
type VariableAttribute,
VariableCharacteristics,
type VariableMonitoring,
} from '../layers/sequelize';
import { type AuthorizationRestrictions, type VariableAttributeQuerystring } from '.';
import { type Authorization, type Boot, type Certificate, ChargingNeeds, type ChargingStation, type Component, type EventData, Evse, type Location, type SecurityEvent, type Variable, type VariableMonitoring } from '../layers/sequelize';
import { MessageInfo } from '../layers/sequelize';
import { Subscription } from '../layers/sequelize';
import { Tariff } from '../layers/sequelize';
import { TariffQueryString } from './queries/Tariff';
import { ChargingProfile } from '../layers/sequelize';
import { Reservation } from '../layers/sequelize';
import { LocalListVersion } from '../layers/sequelize/model/Authorization/LocalListVersion';
import { SendLocalList } from '../layers/sequelize/model/Authorization/SendLocalList';
import { InstalledCertificate } from '../layers/sequelize/model/Certificate/InstalledCertificate';

export interface IAuthorizationRepository extends CrudRepository<AuthorizationData> {
createOrUpdateByQuerystring: (value: AuthorizationData, query: AuthorizationQuerystring) => Promise<Authorization | undefined>;
Expand Down Expand Up @@ -171,6 +191,8 @@ export interface ICertificateRepository extends CrudRepository<Certificate> {
createOrUpdateCertificate(certificate: Certificate): Promise<Certificate>;
}

export interface IInstalledCertificateRepository extends CrudRepository<InstalledCertificate> {}

export interface IChargingProfileRepository extends CrudRepository<ChargingProfile> {
createOrUpdateChargingProfile(chargingProfile: ChargingProfileType, stationId: string, evseId?: number | null, chargingLimitSource?: ChargingLimitSourceEnumType, isActive?: boolean): Promise<ChargingProfile>;
createChargingNeeds(chargingNeeds: NotifyEVChargingNeedsRequest, stationId: string): Promise<ChargingNeeds>;
Expand Down
3 changes: 2 additions & 1 deletion 01_Data/src/layers/sequelize/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export { ChargingStationSequence } from './model/ChargingStationSequence';
export { MessageInfo } from './model/MessageInfo';
export { Tariff } from './model/Tariff';
export { Subscription } from './model/Subscription';
export { Certificate, SignatureAlgorithmEnumType, CountryNameEnumType } from './model/Certificate';
export { Certificate, SignatureAlgorithmEnumType, CountryNameEnumType, InstalledCertificate } from './model/Certificate';
export { ChargingProfile, ChargingNeeds, ChargingSchedule, CompositeSchedule, SalesTariff } from './model/ChargingProfile';
export { CallMessage } from './model/CallMessage';
export { Reservation } from './model/Reservation';
Expand All @@ -34,6 +34,7 @@ export { SequelizeMessageInfoRepository } from './repository/MessageInfo';
export { SequelizeTariffRepository } from './repository/Tariff';
export { SequelizeSubscriptionRepository } from './repository/Subscription';
export { SequelizeCertificateRepository } from './repository/Certificate';
export { SequelizeInstalledCertificateRepository } from './repository/InstalledCertificate';
export { SequelizeChargingProfileRepository } from './repository/ChargingProfile';
export { SequelizeCallMessageRepository } from './repository/CallMessage';
export { SequelizeReservationRepository } from './repository/Reservation';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { CertificateHashDataType, GetCertificateIdUseEnumType, HashAlgorithmEnumType, Namespace } from '@citrineos/base';
import { Column, DataType, ForeignKey, Model, Table } from 'sequelize-typescript';
import { ChargingStation } from '../Location';

@Table
export class InstalledCertificate extends Model implements CertificateHashDataType {
static readonly MODEL_NAME: string = Namespace.InstalledCertificate;

@ForeignKey(() => ChargingStation)
@Column({
type: DataType.STRING(36),
allowNull: false,
})
declare stationId: string;

@Column({
type: DataType.STRING,
allowNull: false,
})
declare hashAlgorithm: HashAlgorithmEnumType;

@Column({
type: DataType.STRING,
allowNull: false,
})
declare issuerNameHash: string;

@Column({
type: DataType.STRING,
allowNull: false,
})
declare issuerKeyHash: string;

@Column({
type: DataType.STRING,
allowNull: false,
})
declare serialNumber: string;

@Column({
type: DataType.ENUM('V2GRootCertificate', 'MORootCertificate', 'CSMSRootCertificate', 'V2GCertificateChain', 'ManufacturerRootCertificate'),
allowNull: false,
})
declare certificateType: GetCertificateIdUseEnumType;
}
1 change: 1 addition & 0 deletions 01_Data/src/layers/sequelize/model/Certificate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache 2.0

export { Certificate } from './Certificate';
export { InstalledCertificate } from './InstalledCertificate';

export const enum SignatureAlgorithmEnumType {
RSA = 'SHA256withRSA',
Expand Down
4 changes: 4 additions & 0 deletions 01_Data/src/layers/sequelize/repository/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ export class SequelizeRepository<T extends Model<any, any>> extends CrudReposito
return await value.save();
}

protected async _bulkCreate(values: T[]): Promise<T[]> {
return await (this.s.models[this.namespace] as ModelStatic<T>).bulkCreate(values as any);
}

protected async _createByKey(value: T, key: string): Promise<T> {
const primaryKey = this.s.models[this.namespace].primaryKeyAttribute;
value.setDataValue(primaryKey, key);
Expand Down
16 changes: 16 additions & 0 deletions 01_Data/src/layers/sequelize/repository/InstalledCertificate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright Contributors to the CitrineOS Project
//
// SPDX-License-Identifier: Apache 2.0

import { SequelizeRepository } from './Base';
import { IInstalledCertificateRepository } from '../../../interfaces';
import { SystemConfig } from '@citrineos/base';
import { Sequelize } from 'sequelize-typescript';
import { ILogObj, Logger } from 'tslog';
import { InstalledCertificate } from '../model/Certificate/InstalledCertificate';

export class SequelizeInstalledCertificateRepository extends SequelizeRepository<InstalledCertificate> implements IInstalledCertificateRepository {
constructor(config: SystemConfig, logger?: Logger<ILogObj>, sequelizeInstance?: Sequelize) {
super(config, InstalledCertificate.MODEL_NAME, logger, sequelizeInstance);
}
}
2 changes: 2 additions & 0 deletions 01_Data/src/layers/sequelize/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
Evse,
IdToken,
IdTokenInfo,
InstalledCertificate,
Location,
MeterValue,
Reservation,
Expand Down Expand Up @@ -118,6 +119,7 @@ export class DefaultSequelizeInstance {
Boot,
CallMessage,
Certificate,
InstalledCertificate,
ChargingNeeds,
ChargingProfile,
ChargingSchedule,
Expand Down
2 changes: 1 addition & 1 deletion 03_Modules/Certificates/src/module/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
Namespace,
WebsocketServerConfig,
} from '@citrineos/base';
import jsrsasign from 'jsrsasign';
import { FastifyInstance, FastifyRequest } from 'fastify';
import { ILogObj, Logger } from 'tslog';
import { ICertificatesModuleApi } from './interface';
Expand All @@ -46,7 +47,6 @@ import {
} from '@citrineos/data';
import fs from 'fs';
import moment from 'moment';
import jsrsasign from 'jsrsasign';

const enum PemType {
Root = 'Root',
Expand Down
Loading