Skip to content

Commit 0f9e783

Browse files
committed
Squashed commit of the following:
commit 84dc0f8 Author: Sławomir Rosiek <[email protected]> Date: Wed Oct 5 11:21:27 2022 +0200 Updated package-lock using npm6 commit 809fa7c Author: Sławomir Rosiek <[email protected]> Date: Tue Oct 4 12:42:33 2022 +0200 Added missing await commit bcc0af5 Author: Sławomir Rosiek <[email protected]> Date: Tue Oct 4 12:39:49 2022 +0200 Interface to store client secret * Added interface + plain text implmentation to store client secret for fiware data target * Removed clientSecret from response in data target controller * Updating clientSecret only if new was provided commit 3539b71 Author: Sławomir Rosiek <[email protected]> Date: Tue Sep 27 13:16:33 2022 +0200 Refactored lambda to be named function commit 07d3f0a Author: Sławomir Rosiek <[email protected]> Date: Tue Sep 27 10:34:23 2022 +0200 Added comment for retries commit 8663114 Author: Sławomir Rosiek <[email protected]> Date: Mon Sep 26 13:15:03 2022 +0200 Removing clientSecret from cache key commit 2c4adc4 Author: Sławomir Rosiek <[email protected]> Date: Mon Sep 26 13:06:30 2022 +0200 Renamed variable + added some note with explanation commit c33cb6c Author: Sławomir Rosiek <[email protected]> Date: Mon Sep 26 13:01:27 2022 +0200 Rethrowing error commit 891ff9e Author: Sławomir Rosiek <[email protected]> Date: Mon Sep 26 12:58:51 2022 +0200 Changed scope of the type + awaiting cache manager commit c90ed06 Author: Sławomir Rosiek <[email protected]> Date: Mon Sep 26 12:53:35 2022 +0200 Added clearing and retrying in case token is invalid commit 1dc9c54 Author: Sławomir Rosiek <[email protected]> Date: Thu Sep 22 11:13:32 2022 +0200 Refactored AuthenticationTokenProvider to use more common aproach for fetching access token commit 339828d Author: Sławomir Rosiek <[email protected]> Date: Wed Sep 21 14:47:23 2022 +0200 Fixed import statement commit 41f301d Author: Sławomir Rosiek <[email protected]> Date: Wed Sep 21 14:47:10 2022 +0200 Making optional properties nullable commit ba62f2d Author: Sławomir Rosiek <[email protected]> Date: Wed Sep 21 14:14:40 2022 +0200 Fixed formatting for fiware-data-target.entity.ts commit 9d04eda Author: Sławomir Rosiek <[email protected]> Date: Wed Sep 21 12:47:35 2022 +0200 Extracted AuthenticationTokenProvider to separate file commit 5432e91 Author: Sławomir Rosiek <[email protected]> Date: Wed Sep 21 12:35:57 2022 +0200 Simplified line commit 58420c0 Author: Sławomir Rosiek <[email protected]> Date: Thu Jul 21 08:18:09 2022 +0200 Improved logging commit 2636827 Author: Sławomir Rosiek <[email protected]> Date: Wed Jul 20 15:25:58 2022 +0200 Decresead clockSkew so it wont be same as default ttl of the token commit 7512f34 Author: Sławomir Rosiek <[email protected]> Date: Wed Jul 20 12:10:17 2022 +0200 Added extra logging commit e1e00b5 Author: Sławomir Rosiek <[email protected]> Date: Tue Jul 19 11:19:08 2022 +0200 Fixed logger name for AuthenticationTokenProvider commit 9647cbb Author: Sławomir Rosiek <[email protected]> Date: Fri Jul 15 12:53:54 2022 +0200 Added more diagnostic commit b61efae Author: Sławomir Rosiek <[email protected]> Date: Thu Jul 14 08:58:09 2022 +0200 Updated API to map OAuth autorization property commit 02d4f5d Author: Sławomir Rosiek <[email protected]> Date: Tue Jul 5 14:53:34 2022 +0200 Extending FiWare module to support OAuth authorization. Added new type of authentication OAuth that requires tokenUrl, clientId and clientSecret and using them to obtain a token and attaching it to request sent to FiWare
1 parent 632a56c commit 0f9e783

File tree

12 files changed

+231
-37
lines changed

12 files changed

+231
-37
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,8 @@ coverage-e2e
157157

158158
# secrets
159159
secrets/
160+
161+
# Visual Studio
162+
applicationhost.config
163+
*.suo
164+
.vs/

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"axios-cache-adapter": "^2.5.0",
5151
"bcryptjs": "^2.4.3",
5252
"bluebird": "^3.7.2",
53+
"cache-manager": "^4.0.1",
5354
"class-transformer": "^0.5.1",
5455
"class-validator": "^0.13.2",
5556
"compression": "^1.7.4",
@@ -80,6 +81,7 @@
8081
"@nestjs/testing": "^9.1.2",
8182
"@types/bcryptjs": "^2.4.2",
8283
"@types/bluebird": "^3.5.33",
84+
"@types/cache-manager": "^4.0.1",
8385
"@types/compression": "^1.7.0",
8486
"@types/cookie-parser": "^1.4.2",
8587
"@types/cron": "^1.7.2",

src/entities/dto/create-data-target.dto.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ export class CreateDataTargetDto {
5858
@ApiProperty({ required: false, default: "", example: null })
5959
authorizationHeader: string;
6060

61+
@ApiProperty({ required: false })
62+
tokenEndpoint?: string;
63+
64+
@ApiProperty({ required: false })
65+
clientId?: string;
66+
67+
@ApiProperty({ required: false })
68+
clientSecret?: string;
69+
6170
@ApiPropertyOptional({ required: false })
6271
@IsOptional()
6372
@ValidateNested({ each: true })

src/entities/enum/authorization-type.enum.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export enum AuthorizationType {
22
NO_AUTHORIZATION = "NO_AUTHORIZATION",
33
HTTP_BASIC_AUTHORIZATION = "HTTP_BASIC_AUTHORIZATION",
44
HEADER_BASED_AUTHORIZATION = "HEADER_BASED_AUTHORIZATION",
5+
OAUTH_AUTHORIZATION = "OAUTH_AUTHORIZATION"
56
}

src/entities/fiware-data-target.entity.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { FiwareDataTargetConfiguration } from "./interfaces/fiware-data-target-c
99
@ChildEntity(DataTargetType.Fiware)
1010
export class FiwareDataTarget extends DataTarget {
1111
@Column()
12-
url: string;
12+
url: string;
1313

1414
@Column({ default: 30000, comment: "HTTP call timeout in milliseconds" })
1515
timeout: number;
@@ -23,22 +23,36 @@ export class FiwareDataTarget extends DataTarget {
2323
@Column({ nullable: true })
2424
context: string;
2525

26+
@Column({ nullable: true })
27+
clientId?: string;
28+
29+
@Column({ nullable: true, select: false })
30+
clientSecret?: string;
31+
32+
@Column({ nullable: true })
33+
tokenEndpoint?: string;
34+
2635
@BeforeInsert()
27-
private beforeInsert() {
36+
private beforeInsert() {
2837
this.type = DataTargetType.Fiware;
2938
}
3039

3140
toConfiguration(): FiwareDataTargetConfiguration {
3241
return {
3342
url: this.url,
3443
timeout: this.timeout,
35-
authorizationType:
36-
this.authorizationHeader != ""
37-
? AuthorizationType.HEADER_BASED_AUTHORIZATION
38-
: AuthorizationType.NO_AUTHORIZATION,
44+
authorizationType: this.tokenEndpoint
45+
? AuthorizationType.OAUTH_AUTHORIZATION
46+
: this.authorizationHeader
47+
? AuthorizationType.HEADER_BASED_AUTHORIZATION
48+
: AuthorizationType.NO_AUTHORIZATION,
3949
authorizationHeader: this.authorizationHeader,
4050
tenant: this.tenant,
41-
context: this.context
51+
context: this.context,
52+
clientId: this.clientId,
53+
clientSecret: this.clientSecret,
54+
tokenEndpoint: this.tokenEndpoint,
55+
updatedAt: this.updatedAt,
4256
};
4357
}
4458
}

src/entities/interfaces/fiware-data-target-configuration.interface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ export interface FiwareDataTargetConfiguration {
99
authorizationHeader?: string;
1010
tenant?: string;
1111
context?: string;
12+
clientId?: string;
13+
clientSecret?: string;
14+
tokenEndpoint?: string;
15+
updatedAt: Date;
1216
}

src/helpers/fiware-token.helper.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { HttpService } from "@nestjs/axios";
2+
import { CACHE_MANAGER, Inject, Injectable, Logger } from "@nestjs/common"
3+
import { Cache } from 'cache-manager'
4+
import { Client } from "mqtt";
5+
import { FiwareDataTargetConfiguration } from "../entities/interfaces/fiware-data-target-configuration.interface";
6+
7+
type TokenEndpointResponse = {
8+
data: {
9+
access_token: string,
10+
expires_in: number,
11+
}
12+
}
13+
14+
export const CLIENT_SECRET_PROVIDER = 'ClientSecretProvider'
15+
export interface ClientSecretProvider {
16+
getClientSecret(secretRef: string): Promise<string>
17+
store(secret: string): Promise<string>
18+
}
19+
20+
@Injectable()
21+
export class PlainTextClientSecretProvider implements ClientSecretProvider {
22+
getClientSecret(secretRef: string): Promise<string> {
23+
return Promise.resolve(secretRef)
24+
}
25+
26+
store(secret: string): Promise<string> {
27+
return Promise.resolve(secret)
28+
}
29+
}
30+
31+
@Injectable()
32+
export class AuthenticationTokenProvider {
33+
34+
private readonly logger = new Logger(AuthenticationTokenProvider.name);
35+
36+
constructor(
37+
private httpService: HttpService,
38+
@Inject(CLIENT_SECRET_PROVIDER) private clientSecretProvider: ClientSecretProvider,
39+
@Inject(CACHE_MANAGER) private cacheManager: Cache) {
40+
}
41+
42+
async clearConfig(config: FiwareDataTargetConfiguration): Promise<void> {
43+
if (config.clientId) {
44+
this.logger.debug(`AuthenticationTokenProvider clearing token for ${config.clientId}`)
45+
const key = config.clientId + config.updatedAt.getTime()
46+
return this.cacheManager.del(key)
47+
}
48+
}
49+
50+
async getToken(config: FiwareDataTargetConfiguration): Promise<string> {
51+
const key = config.clientId + config.updatedAt.getTime();
52+
53+
const token = await this.cacheManager.get<string>(key)
54+
if (token) {
55+
return token
56+
} else {
57+
try {
58+
const clientSecret = await this.clientSecretProvider.getClientSecret(config.clientSecret)
59+
const params = new URLSearchParams([
60+
['grant_type', 'client_credentials'],
61+
['client_id', config.clientId],
62+
['client_secret', clientSecret]
63+
])
64+
const { data }: TokenEndpointResponse = await this.httpService.post(config.tokenEndpoint, params, {
65+
headers: {
66+
'Content-Type': 'application/x-www-form-urlencoded',
67+
},
68+
}).toPromise()
69+
70+
// NOTE: TTL offset include some time for clock differences between authentication server and local server + network delay
71+
const ttlOffset = 30
72+
const ttl = data.expires_in - ttlOffset
73+
this.logger.debug(`AuthenticationTokenProvider caching token for ${config.clientId} (expires in ${ttl} seconds)`)
74+
await this.cacheManager.set(key, data.access_token, { ttl })
75+
return data.access_token
76+
}
77+
catch (err) {
78+
this.logger.error(`AuthenticationTokenProvider got error ${err}`)
79+
throw err
80+
}
81+
}
82+
}
83+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {MigrationInterface, QueryRunner} from "typeorm";
2+
3+
export class fiwareOauthAuthorization1657016879520 implements MigrationInterface {
4+
name = 'fiwareOauthAuthorization1657016879520'
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(`ALTER TABLE "data_target" ADD "clientId" character varying`);
8+
await queryRunner.query(`ALTER TABLE "data_target" ADD "clientSecret" character varying`);
9+
await queryRunner.query(`ALTER TABLE "data_target" ADD "tokenEndpoint" character varying`);
10+
}
11+
12+
public async down(queryRunner: QueryRunner): Promise<void> {
13+
await queryRunner.query(`ALTER TABLE "data_target" DROP COLUMN "tokenEndpoint"`);
14+
await queryRunner.query(`ALTER TABLE "data_target" DROP COLUMN "clientSecret"`);
15+
await queryRunner.query(`ALTER TABLE "data_target" DROP COLUMN "clientId"`);
16+
}
17+
18+
}
Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
import { HttpModule } from "@nestjs/axios";
2-
import { Module } from "@nestjs/common";
2+
import { CacheModule, Module } from "@nestjs/common";
33
import { FiwareDataTargetService } from "@services/data-targets/fiware-data-target.service";
4+
import { AuthenticationTokenProvider, CLIENT_SECRET_PROVIDER, PlainTextClientSecretProvider } from "../../helpers/fiware-token.helper";
45

56
@Module({
6-
imports: [HttpModule],
7-
providers: [FiwareDataTargetService],
8-
exports: [FiwareDataTargetService],
7+
imports: [HttpModule, CacheModule.register()],
8+
providers: [
9+
FiwareDataTargetService,
10+
AuthenticationTokenProvider,
11+
{
12+
provide: CLIENT_SECRET_PROVIDER,
13+
useClass: PlainTextClientSecretProvider,
14+
},
15+
],
16+
exports: [
17+
FiwareDataTargetService,
18+
{
19+
provide: CLIENT_SECRET_PROVIDER,
20+
useClass: PlainTextClientSecretProvider,
21+
},
22+
],
923
})
1024
export class DataTargetFiwareSenderModule {}

src/modules/device-management/data-target.module.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ import { DataTargetController } from "@admin-controller/data-target.controller";
44
import { ApplicationModule } from "@modules/device-management/application.module";
55
import { SharedModule } from "@modules/shared.module";
66
import { DataTargetService } from "@services/data-targets/data-target.service";
7+
import { CLIENT_SECRET_PROVIDER, PlainTextClientSecretProvider } from "../../helpers/fiware-token.helper";
78

89
@Module({
910
imports: [SharedModule, ApplicationModule],
1011
exports: [DataTargetService],
1112
controllers: [DataTargetController],
12-
providers: [DataTargetService],
13+
providers: [DataTargetService, {
14+
provide: CLIENT_SECRET_PROVIDER,
15+
useClass: PlainTextClientSecretProvider
16+
}],
1317
})
1418
export class DataTargetModule {}

0 commit comments

Comments
 (0)