Skip to content

Commit

Permalink
fix: commit build mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
blimmer committed Apr 5, 2024
1 parent b951aee commit f44142e
Show file tree
Hide file tree
Showing 9 changed files with 1,251 additions and 1,186 deletions.
18 changes: 9 additions & 9 deletions .projenrc.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { awscdk } from 'projen';
import { awscdk } from "projen";

const project = new awscdk.AwsCdkConstructLibrary({
author: 'Ben Limmer',
authorAddress: '[email protected]',
cdkVersion: '2.1.0',
defaultReleaseBranch: 'main',
name: '@blimmer/cdk-circleci-oidc',
repositoryUrl: 'https://github.com/blimmer/cdk-circleci-oidc.git',
author: "Ben Limmer",
authorAddress: "[email protected]",
cdkVersion: "2.1.0",
defaultReleaseBranch: "main",
name: "@blimmer/cdk-circleci-oidc",
repositoryUrl: "https://github.com/blimmer/cdk-circleci-oidc.git",

projenrcTs: true,

jsiiVersion: "~5.0.0",

python: {
distName: 'cdk-circleci-oidc',
module: 'cdk_circleci_oidc',
distName: "cdk-circleci-oidc",
module: "cdk_circleci_oidc",
},

depsUpgrade: false,
Expand Down
6 changes: 3 additions & 3 deletions API.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 9 additions & 6 deletions src/CircleCiOidcProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CfnOIDCProvider, OpenIdConnectProvider } from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';
import { ManualCircleCiOidcProviderProps } from './CircleCiOidcRole';
import { CfnOIDCProvider, OpenIdConnectProvider } from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs";
import { ManualCircleCiOidcProviderProps } from "./CircleCiOidcRole";

export interface CircleCiOidcProviderProps {
/**
Expand Down Expand Up @@ -34,13 +34,13 @@ export class CircleCiOidcProvider extends Construct {

const {
organizationId,
circleCiOidcThumbprints = ['9e99a48a9960b14926bb7f3b02e22da2b0ab7280'],
circleCiOidcThumbprints = ["9e99a48a9960b14926bb7f3b02e22da2b0ab7280"],
} = props;

// The L2 construct uses a Custom Resource, which is slow and has a few known issues
// (see https://github.com/aws/aws-cdk/issues/21197#issuecomment-1312843734)
// Therefore, we use the L1 OIDC provider construct directly instead.
this.provider = new CfnOIDCProvider(this, 'CircleCiOidcProvider', {
this.provider = new CfnOIDCProvider(this, "CircleCiOidcProvider", {
url: `https://oidc.circleci.com/org/${organizationId}`,
clientIdList: [organizationId],
thumbprintList: circleCiOidcThumbprints,
Expand All @@ -49,7 +49,10 @@ export class CircleCiOidcProvider extends Construct {
this.organizationId = organizationId;
}

public getProviderForExport(accountId: string, importName = 'CircleCiOidcProviderForExport'): ManualCircleCiOidcProviderProps {
public getProviderForExport(
accountId: string,
importName = "CircleCiOidcProviderForExport",
): ManualCircleCiOidcProviderProps {
return {
provider: OpenIdConnectProvider.fromOpenIdConnectProviderArn(
this,
Expand Down
54 changes: 43 additions & 11 deletions src/CircleCiOidcRole.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { Condition, IManagedPolicy, IOpenIdConnectProvider, OpenIdConnectPrincipal, OpenIdConnectProvider, PolicyDocument, Role } from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';
import { CircleCiOidcProvider } from './CircleCiOidcProvider';
import {
Condition,
IManagedPolicy,
IOpenIdConnectProvider,
OpenIdConnectPrincipal,
OpenIdConnectProvider,
PolicyDocument,
Role,
} from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs";
import { CircleCiOidcProvider } from "./CircleCiOidcProvider";

/**
* If you're using the {@link CircleCiOidcProvider} construct, pass it instead of these manually-defined props.
Expand All @@ -19,7 +27,9 @@ export interface ManualCircleCiOidcProviderProps {
}

export interface CircleCiOidcRoleProps {
readonly circleCiOidcProvider: CircleCiOidcProvider | ManualCircleCiOidcProviderProps;
readonly circleCiOidcProvider:
| CircleCiOidcProvider
| ManualCircleCiOidcProviderProps;

/**
* Provide the UUID(s) of the CircleCI project(s) you want to be allowed to use this role. If you don't provide this
Expand Down Expand Up @@ -58,33 +68,55 @@ export class CircleCiOidcRole extends Construct {
super(scope, id);

const { circleCiProjectIds, circleCiOidcProvider, ...roleProps } = props;
const { provider, organizationId } = this.extractOpenIdConnectProvider(circleCiOidcProvider);
const { provider, organizationId } =
this.extractOpenIdConnectProvider(circleCiOidcProvider);
const oidcUrl = `oidc.circleci.com/org/${organizationId}`;

this.role = new Role(this, 'CircleCiOidcRole', {
this.role = new Role(this, "CircleCiOidcRole", {
assumedBy: new OpenIdConnectPrincipal(provider, {
StringEquals: { [`${oidcUrl}:aud`]: organizationId },
...this.generateProjectCondition(oidcUrl, organizationId, circleCiProjectIds),
...this.generateProjectCondition(
oidcUrl,
organizationId,
circleCiProjectIds,
),
}),
...roleProps,
});
}

private extractOpenIdConnectProvider(provider: CircleCiOidcProvider | ManualCircleCiOidcProviderProps) {
private extractOpenIdConnectProvider(
provider: CircleCiOidcProvider | ManualCircleCiOidcProviderProps,
) {
if (provider instanceof CircleCiOidcProvider) {
return { provider: OpenIdConnectProvider.fromOpenIdConnectProviderArn(this, 'ImportOidcProvider', provider.provider.attrArn), organizationId: provider.organizationId };
return {
provider: OpenIdConnectProvider.fromOpenIdConnectProviderArn(
this,
"ImportOidcProvider",
provider.provider.attrArn,
),
organizationId: provider.organizationId,
};
} else {
return provider;
}
}

private generateProjectCondition(oidcUrl: string, organizationId: string, circleCiProjectIds?: string[]): Condition {
private generateProjectCondition(
oidcUrl: string,
organizationId: string,
circleCiProjectIds?: string[],
): Condition {
if (!circleCiProjectIds || circleCiProjectIds.length === 0) {
return {};
}

return {
StringLike: { [`${oidcUrl}:sub`]: circleCiProjectIds.map((projectId) => `org/${organizationId}/project/${projectId}/*`) },
StringLike: {
[`${oidcUrl}:sub`]: circleCiProjectIds.map(
(projectId) => `org/${organizationId}/project/${projectId}/*`,
),
},
};
}
}
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './CircleCiOidcProvider';
export * from './CircleCiOidcRole';
export * from "./CircleCiOidcProvider";
export * from "./CircleCiOidcRole";
52 changes: 28 additions & 24 deletions test/CircleCiOidcProvider.test.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,47 @@
import { App, Stack } from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import { CircleCiOidcProvider } from '../src';
import { App, Stack } from "aws-cdk-lib";
import { Template } from "aws-cdk-lib/assertions";
import { CircleCiOidcProvider } from "../src";

describe('CircleCiOidcProvider', () => {
it('uses the organization ID as the client ID', () => {
describe("CircleCiOidcProvider", () => {
it("uses the organization ID as the client ID", () => {
const app = new App();
const stack = new Stack(app, 'TestStack');
new CircleCiOidcProvider(stack, 'CircleCiOidcProvider', {
organizationId: '1234',
const stack = new Stack(app, "TestStack");
new CircleCiOidcProvider(stack, "CircleCiOidcProvider", {
organizationId: "1234",
});

Template.fromStack(stack).hasResourceProperties('AWS::IAM::OIDCProvider', {
ClientIdList: ['1234'],
Template.fromStack(stack).hasResourceProperties("AWS::IAM::OIDCProvider", {
ClientIdList: ["1234"],
});
});
it('uses a default thumbprint list', () => {
it("uses a default thumbprint list", () => {
const app = new App();
const stack = new Stack(app, 'TestStack');
new CircleCiOidcProvider(stack, 'CircleCiOidcProvider', {
organizationId: '1234',
const stack = new Stack(app, "TestStack");
new CircleCiOidcProvider(stack, "CircleCiOidcProvider", {
organizationId: "1234",
});

Template.fromStack(stack).hasResourceProperties('AWS::IAM::OIDCProvider', {
ThumbprintList: ['9e99a48a9960b14926bb7f3b02e22da2b0ab7280'],
Template.fromStack(stack).hasResourceProperties("AWS::IAM::OIDCProvider", {
ThumbprintList: ["9e99a48a9960b14926bb7f3b02e22da2b0ab7280"],
});
});

it('can export a provider for use in other stacks', () => {
it("can export a provider for use in other stacks", () => {
const app = new App();
const stack = new Stack(app, 'TestStack');
const provider = new CircleCiOidcProvider(stack, 'CircleCiOidcProvider', {
organizationId: '1234',
const stack = new Stack(app, "TestStack");
const provider = new CircleCiOidcProvider(stack, "CircleCiOidcProvider", {
organizationId: "1234",
});

const accountId = '123456789012';
const accountId = "123456789012";
const providerForExport = provider.getProviderForExport(accountId);

expect(providerForExport.organizationId).toEqual('1234');
expect(providerForExport.provider.openIdConnectProviderArn).toEqual('arn:aws:iam::123456789012:oidc-provider/oidc.circleci.com/org/1234');
expect(providerForExport.provider.openIdConnectProviderIssuer).toEqual('oidc.circleci.com/org/1234');
expect(providerForExport.organizationId).toEqual("1234");
expect(providerForExport.provider.openIdConnectProviderArn).toEqual(
"arn:aws:iam::123456789012:oidc-provider/oidc.circleci.com/org/1234",
);
expect(providerForExport.provider.openIdConnectProviderIssuer).toEqual(
"oidc.circleci.com/org/1234",
);
});
});
Loading

0 comments on commit f44142e

Please sign in to comment.