Skip to content
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
14 changes: 14 additions & 0 deletions packages/iam/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ export {
type ListAccessKeysOptions,
type ListAccessKeysResponse,
} from './lib/access-key/list';
export {
attachPolicyToAccessKey,
detachPolicyFromAccessKey,
listPoliciesForAccessKey,
type AttachPolicyToAccessKeyOptions,
type DetachPolicyFromAccessKeyOptions,
type ListPoliciesForAccessKeyOptions,
type ListPoliciesForAccessKeyResponse,
} from './lib/access-key/policy';
export {
removeAccessKey,
type RemoveAccessKeyOptions,
Expand All @@ -21,6 +30,11 @@ export {
revokeAllBucketRoles,
type RevokeAllBucketRolesOptions,
} from './lib/access-key/revoke';
export {
rotateAccessKey,
type RotateAccessKeyOptions,
type RotateAccessKeyResponse,
} from './lib/access-key/rotate';
export {
createOrganization,
type CreateOrganizationOptions,
Expand Down
5 changes: 4 additions & 1 deletion packages/iam/src/lib/access-key/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ export async function listAccessKeys(
const formData = new URLSearchParams();
formData.append('Action', 'ListAccessKeys');
formData.append('MaxItems', options?.limit?.toString() ?? '1000');
formData.append('Marker', options?.paginationToken ?? '0');

if (options?.paginationToken) {
formData.append('Marker', options.paginationToken);
}

const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
Expand Down
140 changes: 140 additions & 0 deletions packages/iam/src/lib/access-key/policy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { createIAMClient, IAM_ENDPOINTS } from '../http-client';
import { TigrisIAMConfig, TigrisIAMResponse } from '../types';

export type AttachPolicyToAccessKeyOptions = {
config?: TigrisIAMConfig;
};

export async function attachPolicyToAccessKey(
accessKeyId: string,
policyArn: string,
options?: AttachPolicyToAccessKeyOptions
): Promise<TigrisIAMResponse<void, Error>> {
const { data: client, error } = createIAMClient(options?.config);
if (error) {
return { error };
}

const formData = new URLSearchParams();
formData.append('PolicyArn', policyArn);
formData.append('UserName', accessKeyId);
Comment thread
designcode marked this conversation as resolved.

const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
};

const response = await client.request<URLSearchParams, unknown>({
method: 'POST',
path: IAM_ENDPOINTS.attachPolicy,
body: formData,
headers,
});

if (response.error) {
return { error: response.error };
}

return { data: undefined };
}

export type DetachPolicyFromAccessKeyOptions = {
config?: TigrisIAMConfig;
};

export async function detachPolicyFromAccessKey(
accessKeyId: string,
policyArn: string,
options?: DetachPolicyFromAccessKeyOptions
): Promise<TigrisIAMResponse<void, Error>> {
const { data: client, error } = createIAMClient(options?.config);
if (error) {
return { error };
}

const formData = new URLSearchParams();
formData.append('PolicyArn', policyArn);
formData.append('UserName', accessKeyId);

const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
};

const response = await client.request<URLSearchParams, unknown>({
method: 'POST',
path: IAM_ENDPOINTS.detachPolicy,
body: formData,
headers,
});

if (response.error) {
return { error: response.error };
}

return { data: undefined };
}

export type ListPoliciesForAccessKeyOptions = {
config?: TigrisIAMConfig;
paginationToken?: string;
limit?: number;
};

export type ListPoliciesForAccessKeyResponse = {
paginationToken?: string;
policies: string[]; // policy names
};

type ListPoliciesForAccessKeyApiResponse = {
ListUserPoliciesResult: {
IsTruncated: boolean;
Marker: string;
PolicyNames: string[];
};
};

export async function listPoliciesForAccessKey(
accessKeyId: string,
options?: ListPoliciesForAccessKeyOptions
): Promise<TigrisIAMResponse<ListPoliciesForAccessKeyResponse, Error>> {
const { data: client, error } = createIAMClient(options?.config);
if (error) {
return { error };
}

const formData = new URLSearchParams();
formData.append('UserName', accessKeyId);
formData.append('MaxItems', options?.limit?.toString() ?? '1000');
if (options?.paginationToken) {
formData.append('Marker', options.paginationToken);
}

const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
};

const response = await client.request<
URLSearchParams,
ListPoliciesForAccessKeyApiResponse
>({
method: 'POST',
path: IAM_ENDPOINTS.listPoliciesForAccessKey,
body: formData,
headers,
});

if (response.error) {
return { error: response.error };
}

return {
data: {
paginationToken: response.data.ListUserPoliciesResult.IsTruncated
? response.data.ListUserPoliciesResult.Marker || undefined
: undefined,
policies: response.data.ListUserPoliciesResult.PolicyNames ?? [],
},
};
}
68 changes: 68 additions & 0 deletions packages/iam/src/lib/access-key/rotate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { createIAMClient, IAM_ENDPOINTS } from '../http-client';
import { TigrisIAMConfig, TigrisIAMResponse } from '../types';

export type RotateAccessKeyOptions = {
config?: TigrisIAMConfig;
};

export type RotateAccessKeyResponse = {
id: string;
newSecret: string;
};

type IAMRotateAccessKeyResponse = {
message: string;
rotate_access_key_result: {
id: string;
new_secret: string;
};
status: string;
};

export async function rotateAccessKey(
accessKeyId: string,
options?: RotateAccessKeyOptions
): Promise<TigrisIAMResponse<RotateAccessKeyResponse, Error>> {
const { data: client, error } = createIAMClient(options?.config);
if (error) {
return { error };
}

const formData = new URLSearchParams();
formData.append('Action', 'RotateAccessKey');
Comment thread
designcode marked this conversation as resolved.
formData.append('AccessKeyId', accessKeyId);

const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
};

const response = await client.request<
URLSearchParams,
IAMRotateAccessKeyResponse
>({
method: 'POST',
path: IAM_ENDPOINTS.rotateAccessKey,
body: formData,
headers,
});

if (response.error || response.data?.status !== 'success') {
return {
error:
response.error ??
new Error(response.data?.message ?? 'Unable to rotate access key'),
};
}

const id = response.data?.rotate_access_key_result?.id;
const newSecret = response.data?.rotate_access_key_result?.new_secret;

if (!id || !newSecret) {
return {
error: new Error('Invalid response: missing access key id or secret'),
};
}

return { data: { id, newSecret } };
}
6 changes: 6 additions & 0 deletions packages/iam/src/lib/http-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@ export const IAM_ENDPOINTS = {
listAccessKeys: '/?Detailed',
removeAccessKey: '/?Action=DeleteAccessKey',
revokeAccessKey: '/?Action=UpdateAccessKeyWithBucketsRole',
rotateAccessKey: '/?Action=RotateAccessKey',
// Policies
addPolicy: '/?Action=CreatePolicy',
deletePolicy: '/?Action=ForceDeletePolicy',
editPolicy: '/?Action=UpdatePolicy',
getPolicy: '/?Action=GetPolicyDetailed',
listPolicies: '/?Action=ListPolicies',

// policy and access key
attachPolicy: '/?Action=AttachUserPolicy',
detachPolicy: '/?Action=DetachUserPolicy',
listPoliciesForAccessKey: '/?Action=ListUserPolicies',
};

function getIAMEndpoint(options?: TigrisIAMConfig): string {
Expand Down
16 changes: 13 additions & 3 deletions packages/iam/src/lib/policy/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ export type GetPolicyOptions = {

export type GetPolicyResponse = Policy & {
document: PolicyDocument;
users: string[];
users: {
id: string;
name: string;
}[];
};

type GetPolicyApiResponse = {
Expand All @@ -27,7 +30,10 @@ type GetPolicyApiResponse = {
PolicyName: string;
Tags: null;
UpdateDate: string;
Users: string[];
Users: {
UserId: string;
UserName: string;
}[];
};
};

Expand Down Expand Up @@ -100,7 +106,11 @@ export async function getPolicy(
path: policy.Path,
resource: policy.Arn,
updateDate: new Date(policy.UpdateDate),
users: policy.Users ?? [],
users:
policy.Users?.map((user) => ({
id: user.UserId,
name: user.UserName,
})) ?? [],
},
};
}
5 changes: 4 additions & 1 deletion packages/iam/src/lib/policy/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ export async function listPolicies(

const formData = new URLSearchParams();
formData.append('MaxItems', options?.limit?.toString() ?? '1000');
formData.append('Marker', options?.paginationToken ?? '0');

if (options?.paginationToken) {
formData.append('Marker', options.paginationToken);
}

const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
Expand Down
Loading