Skip to content

Commit

Permalink
improve integration test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
JacksonTian authored and yndu13 committed Sep 2, 2024
1 parent d86986b commit c67f21a
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 100 deletions.
25 changes: 23 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
branches: [ master ]

permissions:
id-token: write

jobs:
build:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -34,11 +37,29 @@ jobs:
with:
token: ${{ secrets.CODECOV_TOKEN }} # required

- name: Setup OIDC
run: npm install @actions/[email protected] @actions/http-client

- name: Get Id Token
uses: actions/github-script@v7
id: idtoken
with:
script: |
const coreDemo = require('@actions/core');
const idToken = await coreDemo.getIDToken('sts.aliyuncs.com');
const fsx = require('fs/promises');
await fsx.writeFile('/tmp/oidc_token', idToken);
- name: Integration Test
run: test -z $SUB_ACCESS_KEY_ID -a -z $SUB_ACCESS_KEY_SECRET || npm run test-integration
run: npm run integration
if: env.SUB_ACCESS_KEY_ID != ''
env:
# for RAM role ARN
ROLE_ARN: ${{ secrets.ROLE_ARN }}
SUB_ACCESS_KEY_ID: ${{ secrets.SUB_ACCESS_KEY_ID }}
SUB_ACCESS_KEY_SECRET: ${{ secrets.SUB_ACCESS_KEY_SECRET }}
ROLE_ARN_TO_ASSUME: ${{ secrets.ROLE_ARN_TO_ASSUME }}

# for OIDC
ALIBABA_CLOUD_OIDC_PROVIDER_ARN: ${{ secrets.ALIBABA_CLOUD_OIDC_PROVIDER_ARN }}
ALIBABA_CLOUD_OIDC_TOKEN_FILE: "/tmp/oidc_token"
ALIBABA_CLOUD_ROLE_ARN: ${{ secrets.OIDC_ROLE_ARN }}
66 changes: 66 additions & 0 deletions integration/credentials.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import assert from 'assert';
import 'mocha';
import CredentialsClient, { Config } from '../src/client';

describe('credentials', () => {
it('RAM Role ARN should ok with ak', async function () {
const config = new Config({
type: 'ram_role_arn',
roleArn: process.env.ROLE_ARN,
accessKeyId: process.env.SUB_ACCESS_KEY_ID,
accessKeySecret: process.env.SUB_ACCESS_KEY_SECRET
});

const client = new CredentialsClient(config, {});
assert.strictEqual(client.getType(), 'ram_role_arn')
const credentials = await client.getCredential();
assert.ok(credentials);
assert.strictEqual(credentials.type, 'ram_role_arn');
assert.ok(credentials.securityToken);
});

it('RAM Role ARN should ok with sts', async function () {
const client = new CredentialsClient(new Config({
type: 'ram_role_arn',
roleArn: process.env.ROLE_ARN,
accessKeyId: process.env.SUB_ACCESS_KEY_ID,
accessKeySecret: process.env.SUB_ACCESS_KEY_SECRET
}));

const credentials = await client.getCredential();
assert.ok(credentials);
assert.strictEqual(credentials.type, 'ram_role_arn');
assert.ok(credentials.securityToken);

// assume anothor role
const config = new Config({
type: 'ram_role_arn',
roleArn: process.env.ROLE_ARN_TO_ASSUME,
accessKeyId: credentials.accessKeyId,
accessKeySecret: credentials.accessKeySecret,
securityToken: credentials.securityToken
});
const client2 = new CredentialsClient(config);
assert.strictEqual(client2.getType(), 'ram_role_arn')
const credentials2 = await client2.getCredential();
assert.ok(credentials2);
assert.strictEqual(credentials2.type, 'ram_role_arn');
assert.ok(credentials2.securityToken);
});

it('OIDC should ok', async function() {
const config = new Config({
type: 'oidc_role_arn',
roleArn: process.env.ALIBABA_CLOUD_ROLE_ARN,
oidcProviderArn: process.env.ALIBABA_CLOUD_OIDC_PROVIDER_ARN,
oidcTokenFilePath: process.env.ALIBABA_CLOUD_OIDC_TOKEN_FILE,
roleSessionName: 'credentials-go-test'
});
const client = new CredentialsClient(config, {});
assert.strictEqual(client.getType(), 'oidc_role_arn')
const credentials = await client.getCredential();
assert.ok(credentials);
assert.strictEqual(credentials.type, 'oidc_role_arn');
assert.ok(credentials.securityToken);
});
});
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"test-dev": "nyc -e .ts -r=html -r=text -r=lcov mocha -b -r ts-node/register",
"cov": "nyc -e .ts -r=html -r=text -r=lcov npm run test",
"ci": "npm run cov",
"test-integration": "mocha -b -r ts-node/register -R spec test/*.integration.ts",
"integration": "mocha -b -r ts-node/register -R spec integration/*.test.ts",
"clean": "rm -rf coverage"
},
"repository": {
Expand All @@ -26,6 +26,7 @@
"author": "Alibaba Cloud SDK",
"license": "MIT",
"devDependencies": {
"@types/debug": "^4.1.12",
"@types/expect.js": "^0.3.29",
"@types/ini": "^1.3.30",
"@types/mocha": "^10.0.6",
Expand Down
11 changes: 9 additions & 2 deletions src/providers/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class ResponseBuilder{
}

constructor() {

this.headers = {};
}

build(): Response {
Expand All @@ -127,7 +127,7 @@ class ResponseBuilder{
function querystringify(queries: {[key: string]: string}) {
const fields = [];
for (const [key, value] of Object.entries(queries)) {
fields.push(key + '=' + value);
fields.push(key + '=' + encodeURIComponent(value));
}
return fields.join('&');
}
Expand All @@ -138,8 +138,15 @@ export async function doRequest(req: Request): Promise<Response> {
url += `?` + querystringify(req.queries)
}

let body;
if (req.bodyForm && Object.keys(req.bodyForm).length > 0) {
body = querystringify(req.bodyForm);
}

const response = await httpx.request(url, {
method: req.method,
data: body,
headers: req.headers
});

const responseBody = await httpx.read(response, '');
Expand Down
36 changes: 26 additions & 10 deletions src/providers/ram_role_arn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import CredentialsProvider from '../credentials_provider'
import { doRequest, Request } from './http';
import { parseUTC } from './time';

import debug from 'debug';

const log = debug('sign');

class Session {
accessKeyId: string;
accessKeySecret: string;
Expand Down Expand Up @@ -198,20 +202,22 @@ export default class RAMRoleARNCredentialsProvider implements CredentialsProvide
}

const keys = Object.keys(signParams).sort();
const stringToSign = `${method}&%2F&${keys.map((key) => {
const stringToSign = `${method}&${encode('/')}&${encode(keys.map((key) => {
return `${encode(key)}=${encode(signParams[key])}`;
}).join('&')}`;
}).join('&'))}`;

log('stringToSign[Client]:');
log(stringToSign);
const secret = credentials.accessKeySecret + '&';
const signature = kitx.sha1(stringToSign, secret, 'base64') as string;
queries['Signature'] = encode(signature);
queries['Signature'] = signature;
builder.withQueries(queries);

const headers = Object.create(null);
// set headers
headers['Accept-Encoding'] = 'identity'
headers['Content-Type'] = 'application/x-www-form-urlencoded'
headers['Content-Type'] = 'application/x-www-form-urlencoded';
headers['x-acs-credentials-provider'] = credentials.providerName
builder.withHeaders(headers);

// if (this.httpOptions) {
// req.connectTimeout = this.httpOptions.connectTimeout;
Expand All @@ -221,17 +227,27 @@ export default class RAMRoleARNCredentialsProvider implements CredentialsProvide

const request = builder.build();

const resonse = await this.doRequest(request);
const response = await this.doRequest(request);

if (response.statusCode != 200) {
if (response.headers['content-type'] && response.headers['content-type'].startsWith('application/json')) {
const body = JSON.parse(response.body.toString('utf8'));
const serverStringToSign = (body.Message as string).slice('Specified signature is not matched with our calculation. server string to sign is:'.length);
log('stringToSign[Server]:')
log(stringToSign)
if (body.Code === 'SignatureDoesNotMatch' && serverStringToSign === stringToSign) {
throw new Error(`the access key secret is invalid`);
}
}

if (resonse.statusCode != 200) {
throw new Error(`refresh session token failed: ${resonse.body.toString('utf8')}`)
throw new Error(`refresh session token failed: ${response.body.toString('utf8')}`)
}

let data;
try {
data = JSON.parse(resonse.body.toString('utf8'));
data = JSON.parse(response.body.toString('utf8'));
} catch (ex) {
throw new Error(`refresh RoleArn sts token err, unmarshal fail: ${resonse.body.toString('utf8')}`);
throw new Error(`refresh RoleArn sts token err, unmarshal fail: ${response.body.toString('utf8')}`);
}

if (!data || !data.Credentials) {
Expand Down
2 changes: 1 addition & 1 deletion src/providers/static_sts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default class StaticSTSCredentialsProvider implements CredentialsProvider
.withAccessKeyId(this.accessKeyId)
.withAccessKeySecret(this.accessKeySecret)
.withSecurityToken(this.securityToken)
.withProviderName('static_sts')
.withProviderName(this.getProviderName())
.build();
}
}
83 changes: 0 additions & 83 deletions test/credentials.integration.ts

This file was deleted.

6 changes: 5 additions & 1 deletion test/providers/ram_role_arn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,11 @@ describe('RAMRoleARNCredentialsProvider', function () {

// case 2: 4xx error
(p as any).doRequest = async function () {
return Response.builder().withStatusCode(400).withBody(Buffer.from('4xx error')).build();
return Response.builder()
.withStatusCode(400)
.withBody(Buffer.from('4xx error'))
.withHeaders({})
.build();
};

try {
Expand Down

0 comments on commit c67f21a

Please sign in to comment.