Skip to content

Commit 4592026

Browse files
committed
feat: add staticAuthService and examples
1 parent b8e1164 commit 4592026

File tree

6 files changed

+125
-2
lines changed

6 files changed

+125
-2
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Authenticate with service account key file credentials
2+
3+
`static-credentials` example provides the code snippet for authenticating to YDB with the static credentials (user and password).
4+
5+
## Running code snippet
6+
```bash
7+
npm run auth:static-credentials -- --connection-string "grpcs://endpoint/?database=database" --user "user" --password "password"
8+
```
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import {main} from '../../utils';
2+
import {options, run} from './snippet';
3+
4+
main(run, options);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {Driver, StaticCredentialsAuthService, Logger} from 'ydb-sdk';
2+
3+
export async function run(logger: Logger, endpoint: string, database: string, args?: any) {
4+
logger.info('Driver initializing...');
5+
const {user, password} = args;
6+
const authService = new StaticCredentialsAuthService(user, password, endpoint)
7+
const driver = new Driver({endpoint, database, authService});
8+
const timeout = 100000;
9+
if (!await driver.ready(timeout)) {
10+
logger.fatal(`Driver has not become ready in ${timeout}ms!`);
11+
process.exit(1);
12+
}
13+
logger.info('Done');
14+
}
15+
16+
export const options = [{
17+
key: 'user',
18+
name: 'user',
19+
description: 'user for YDB authentication',
20+
},{
21+
key: 'password',
22+
name: 'password',
23+
description: 'password for YDB authentication',
24+
}];

examples/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"auth:environ": "node build/auth/environ/index.js",
1111
"auth:metadata-credentials": "node build/auth/metadata-credentials/index.js",
1212
"auth:service-account-credentials": "node build/auth/service-account-credentials/index.js",
13+
"auth:static-credentials": "node build/auth/static-credentials/index.js",
1314
"bulk-upsert": "node build/bulk-upsert/index.js",
1415
"read-table": "node build/read-table/index.js",
1516
"scan-query": "node build/scan-query/index.js",

src/credentials.ts

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import * as grpc from '@grpc/grpc-js';
22
import jwt from 'jsonwebtoken';
33
import {DateTime} from 'luxon';
4-
import {GrpcService, sleep, withTimeout} from "./utils";
4+
import {getOperationPayload, GrpcService, sleep, withTimeout} from "./utils";
55
import {MetadataTokenService} from '@yandex-cloud/nodejs-sdk/dist/token-service/metadata-token-service';
66
import {TokenService} from "@yandex-cloud/nodejs-sdk/dist/types";
7-
import {yandex} from "ydb-sdk-proto";
7+
import {yandex, Ydb} from "ydb-sdk-proto";
88
import {ISslCredentials, makeDefaultSslCredentials} from './ssl-credentials';
99
import IamTokenService = yandex.cloud.iam.v1.IamTokenService;
10+
import AuthServiceResult = Ydb.Auth.LoginResult;
1011
import ICreateIamTokenResponse = yandex.cloud.iam.v1.ICreateIamTokenResponse;
1112

1213
function makeCredentialsMetadata(token: string): grpc.Metadata {
@@ -39,6 +40,90 @@ export class AnonymousAuthService implements IAuthService {
3940
}
4041
}
4142

43+
44+
export class StaticCredentialsAuthService implements IAuthService {
45+
private tokenExpirationTimeout = 30 * 60 * 1000;
46+
private tokenRequestTimeout = 10 * 1000;
47+
private tokenTimestamp: DateTime|null;
48+
private token: string = '';
49+
private tokenUpdateInProgress: Boolean = false;
50+
private user: string;
51+
private password: string;
52+
private endpoint: string
53+
private sslCredentials: ISslCredentials | undefined
54+
55+
private readonly GrpcService = class extends GrpcService<Ydb.Auth.V1.AuthService> {
56+
constructor(endpoint: string, sslCredentials?: ISslCredentials) {
57+
super(endpoint, 'Ydb.Auth.V1.AuthService', Ydb.Auth.V1.AuthService, sslCredentials)
58+
}
59+
60+
login(request: Ydb.Auth.ILoginRequest) {
61+
return this.api.login(request)
62+
}
63+
64+
destroy() { this.api.end() }
65+
66+
}
67+
68+
constructor(user: string, password: string, endpoint: string, sslCredentials?: ISslCredentials) {
69+
this.user = user;
70+
this.password = password;
71+
this.endpoint = endpoint;
72+
this.sslCredentials = sslCredentials
73+
this.tokenTimestamp = null;
74+
}
75+
76+
private get expired() {
77+
return !this.tokenTimestamp || (
78+
DateTime.utc().diff(this.tokenTimestamp).valueOf() > this.tokenExpirationTimeout
79+
);
80+
}
81+
82+
private async sendTokenRequest(): Promise<AuthServiceResult> {
83+
let runtimeAuthService = new this.GrpcService(this.endpoint, this.sslCredentials)
84+
try {
85+
const tokenPromise = runtimeAuthService.login({
86+
user: this.user,
87+
password: this.password
88+
});
89+
const response = await withTimeout<Ydb.Auth.LoginResponse>(tokenPromise, this.tokenRequestTimeout);
90+
const result = AuthServiceResult.decode(getOperationPayload(response));
91+
runtimeAuthService.destroy()
92+
return result
93+
} catch (error) {
94+
throw new Error("Can't login by user and password " + String(error))
95+
}
96+
97+
}
98+
99+
private async updateToken() {
100+
this.tokenUpdateInProgress = true
101+
const {token} = await this.sendTokenRequest();
102+
if (token) {
103+
this.token = token;
104+
this.tokenTimestamp = DateTime.utc();
105+
this.tokenUpdateInProgress = false
106+
} else {
107+
this.tokenUpdateInProgress = false
108+
throw new Error('Received empty token from credentials!');
109+
}
110+
}
111+
112+
private async waitUntilTokenUpdated() {
113+
while (this.tokenUpdateInProgress) { await sleep(1) }
114+
return
115+
}
116+
117+
public async getAuthMetadata(): Promise<grpc.Metadata> {
118+
if (this.expired) {
119+
// block updateToken calls while token updating
120+
if(this.tokenUpdateInProgress) await this.waitUntilTokenUpdated()
121+
else await this.updateToken();
122+
}
123+
return makeCredentialsMetadata(this.token);
124+
}
125+
}
126+
42127
export class TokenAuthService implements IAuthService {
43128
constructor(private token: string) {}
44129

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export {
6565
IamAuthService,
6666
TokenAuthService,
6767
MetadataAuthService,
68+
StaticCredentialsAuthService
6869
} from './credentials';
6970
export {ISslCredentials} from './ssl-credentials';
7071
export {withRetries, RetryParameters} from './retries';

0 commit comments

Comments
 (0)