Skip to content

Commit

Permalink
Merge pull request #147 from jfrog/GH-145-add-patterns-to-oidc-identi…
Browse files Browse the repository at this point in the history
…ty-mapping

Add patterns to OIDC identity mapping
  • Loading branch information
alexhung authored Oct 31, 2024
2 parents 0ab20fd + 1a86f85 commit cd9a35b
Show file tree
Hide file tree
Showing 6 changed files with 431 additions and 52 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 1.16.0 (November 1, 2024). Tested on Artifactory 7.98.7 with Terraform 1.9.8 and OpenTofu 1.8.4

IMPROVEMENTS:

* resource/platform_oidc_identity_mapping: Add `username_pattern` and `groups_pattern` attributes to username and groups patterns. Attribute `scope` is now optional to support patterns. Issue: [#145](https://github.com/jfrog/terraform-provider-platform/issues/145) PR: [#147](https://github.com/jfrog/terraform-provider-platform/pull/147)

## 1.15.1 (October 18, 2024). Tested on Artifactory 7.90.14 with Terraform 1.9.8 and OpenTofu 1.8.3

IMPROVEMENTS:
Expand Down
63 changes: 59 additions & 4 deletions docs/resources/oidc_identity_mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,62 @@ resource "platform_oidc_identity_mapping" "my-github-oidc-group-identity-mapping
expires_in = 7200
}
}
resource "platform_oidc_identity_mapping" "my-github-oidc-project-roles-identity-mapping" {
name = "my-github-oidc-project-role-identity-mapping"
description = "My GitHub OIDC Project role identity mapping"
provider_name = "my-github-oidc-configuration"
priority = 1
claims_json = jsonencode({
"sub" = "repo:humpty/access-oidc-poc:ref:refs/heads/main",
"workflow_ref" = "humpty/access-oidc-poc/.github/workflows/job.yaml@refs/heads/main"
})
token_spec = {
scope = "applied-permissions/roles:my-project:\"Project Admin\",\"Developer\""
audience = "jfrt@* jfac@* jfmc@* jfmd@* jfevt@* jfxfer@* jflnk@* jfint@* jfwks@*"
expires_in = 7200
}
project_key = "my-project"
}
resource "platform_oidc_identity_mapping" "my-github-oidc-username-pattern-identity-mapping" {
name = "my-github-oidc-username-pattern-identity-mapping"
description = "My GitHub OIDC username pattern identity mapping"
provider_name = "my-github-oidc-configuration"
priority = 1
claims_json = jsonencode({
"sub" = "repo:humpty/access-oidc-poc:ref:refs/heads/main",
"workflow_ref" = "humpty/access-oidc-poc/.github/workflows/job.yaml@refs/heads/main"
})
token_spec = {
username_pattern = "{{user}}"
audience = "*@*"
expires_in = 7200
}
}
resource "platform_oidc_identity_mapping" "my-github-oidc-groups-pattern-identity-mapping" {
name = "my-github-oidc-groups-pattern-identity-mapping"
description = "My GitHub OIDC groups pattern identity mapping"
provider_name = "my-github-oidc-configuration"
priority = 1
claims_json = jsonencode({
"sub" = "repo:humpty/access-oidc-poc:ref:refs/heads/main",
"workflow_ref" = "humpty/access-oidc-poc/.github/workflows/job.yaml@refs/heads/main"
})
token_spec = {
groups_pattern = "{{group}}"
audience = "*@*"
expires_in = 7200
}
}
```

<!-- schema generated by tfplugindocs -->
Expand All @@ -70,15 +126,14 @@ resource "platform_oidc_identity_mapping" "my-github-oidc-group-identity-mapping
<a id="nestedatt--token_spec"></a>
### Nested Schema for `token_spec`

Required:

- `scope` (String) Scope of the token. Must start with `applied-permissions/user`, `applied-permissions/admin`, `applied-permissions/roles:`, or `applied-permissions/groups:`. Group names must be comma-separated, double quotes wrapped, e.g. `applied-permissions/groups:\"readers\",\"my-group\",` Role permissions must be comma-separated, double quotes wrapped, e.g. `applied-permissions:roles:"Developer","Viewer". `username` is also required when setting role permission.

Optional:

- `audience` (String) Sets of (space separated) the JFrog services to which the mapping applies. Default value is `*@*`, which applies to all services.
- `expires_in` (Number) Token expiry time in seconds. Default value is 60.
- `groups_pattern` (String) Provide a pattern which is used to map OIDC groups to Artifactory groups.
- `scope` (String) Scope of the token. Must start with `applied-permissions/user`, `applied-permissions/admin`, `applied-permissions/roles:`, or `applied-permissions/groups:`. Group names must be comma-separated, double quotes wrapped, e.g. `applied-permissions/groups:\"readers\",\"my-group\",` Role permissions are only applicable when in project scope and must be comma-separated, double quotes wrapped, e.g. `applied-permissions:roles:<project-key>:"Developer","Viewer". `username` is also required when setting role permission.
- `username` (String) User name of the OIDC user. Not applicable when `scope` is set to `applied-permissions/groups`. Must be set when `scope` is set to `applied-permissions/roles`.
- `username_pattern` (String) Provide a pattern which is used to map OIDC user to Artifactory user.

## Import

Expand Down
20 changes: 10 additions & 10 deletions docs/resources/permission.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ Optional:

Optional:

- `groups` (Attributes Set) Must contain at least one item. (see [below for nested schema](#nestedatt--artifact--actions--groups))
- `users` (Attributes Set) Must contain at least one item. (see [below for nested schema](#nestedatt--artifact--actions--users))
- `groups` (Attributes Set) (see [below for nested schema](#nestedatt--artifact--actions--groups))
- `users` (Attributes Set) (see [below for nested schema](#nestedatt--artifact--actions--users))

<a id="nestedatt--artifact--actions--groups"></a>
### Nested Schema for `artifact.actions.groups`
Expand Down Expand Up @@ -225,8 +225,8 @@ Optional:

Optional:

- `groups` (Attributes Set) Must contain at least one item. (see [below for nested schema](#nestedatt--build--actions--groups))
- `users` (Attributes Set) Must contain at least one item. (see [below for nested schema](#nestedatt--build--actions--users))
- `groups` (Attributes Set) (see [below for nested schema](#nestedatt--build--actions--groups))
- `users` (Attributes Set) (see [below for nested schema](#nestedatt--build--actions--users))

<a id="nestedatt--build--actions--groups"></a>
### Nested Schema for `build.actions.groups`
Expand Down Expand Up @@ -287,8 +287,8 @@ Optional:

Optional:

- `groups` (Attributes Set) Must contain at least one item. (see [below for nested schema](#nestedatt--destination--actions--groups))
- `users` (Attributes Set) Must contain at least one item. (see [below for nested schema](#nestedatt--destination--actions--users))
- `groups` (Attributes Set) (see [below for nested schema](#nestedatt--destination--actions--groups))
- `users` (Attributes Set) (see [below for nested schema](#nestedatt--destination--actions--users))

<a id="nestedatt--destination--actions--groups"></a>
### Nested Schema for `destination.actions.groups`
Expand Down Expand Up @@ -343,8 +343,8 @@ Optional:

Optional:

- `groups` (Attributes Set) Must contain at least one item. (see [below for nested schema](#nestedatt--pipeline_source--actions--groups))
- `users` (Attributes Set) Must contain at least one item. (see [below for nested schema](#nestedatt--pipeline_source--actions--users))
- `groups` (Attributes Set) (see [below for nested schema](#nestedatt--pipeline_source--actions--groups))
- `users` (Attributes Set) (see [below for nested schema](#nestedatt--pipeline_source--actions--users))

<a id="nestedatt--pipeline_source--actions--groups"></a>
### Nested Schema for `pipeline_source.actions.groups`
Expand Down Expand Up @@ -399,8 +399,8 @@ Optional:

Optional:

- `groups` (Attributes Set) Must contain at least one item. (see [below for nested schema](#nestedatt--release_bundle--actions--groups))
- `users` (Attributes Set) Must contain at least one item. (see [below for nested schema](#nestedatt--release_bundle--actions--users))
- `groups` (Attributes Set) (see [below for nested schema](#nestedatt--release_bundle--actions--groups))
- `users` (Attributes Set) (see [below for nested schema](#nestedatt--release_bundle--actions--users))

<a id="nestedatt--release_bundle--actions--groups"></a>
### Nested Schema for `release_bundle.actions.groups`
Expand Down
58 changes: 57 additions & 1 deletion examples/resources/platform_oidc_identity_mapping/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,60 @@ resource "platform_oidc_identity_mapping" "my-github-oidc-group-identity-mapping
audience = "jfrt@* jfac@* jfmc@* jfmd@* jfevt@* jfxfer@* jflnk@* jfint@* jfwks@*"
expires_in = 7200
}
}
}

resource "platform_oidc_identity_mapping" "my-github-oidc-project-roles-identity-mapping" {
name = "my-github-oidc-project-role-identity-mapping"
description = "My GitHub OIDC Project role identity mapping"
provider_name = "my-github-oidc-configuration"
priority = 1

claims_json = jsonencode({
"sub" = "repo:humpty/access-oidc-poc:ref:refs/heads/main",
"workflow_ref" = "humpty/access-oidc-poc/.github/workflows/job.yaml@refs/heads/main"
})

token_spec = {
scope = "applied-permissions/roles:my-project:\"Project Admin\",\"Developer\""
audience = "jfrt@* jfac@* jfmc@* jfmd@* jfevt@* jfxfer@* jflnk@* jfint@* jfwks@*"
expires_in = 7200
}

project_key = "my-project"
}

resource "platform_oidc_identity_mapping" "my-github-oidc-username-pattern-identity-mapping" {
name = "my-github-oidc-username-pattern-identity-mapping"
description = "My GitHub OIDC username pattern identity mapping"
provider_name = "my-github-oidc-configuration"
priority = 1

claims_json = jsonencode({
"sub" = "repo:humpty/access-oidc-poc:ref:refs/heads/main",
"workflow_ref" = "humpty/access-oidc-poc/.github/workflows/job.yaml@refs/heads/main"
})

token_spec = {
username_pattern = "{{user}}"
audience = "*@*"
expires_in = 7200
}
}

resource "platform_oidc_identity_mapping" "my-github-oidc-groups-pattern-identity-mapping" {
name = "my-github-oidc-groups-pattern-identity-mapping"
description = "My GitHub OIDC groups pattern identity mapping"
provider_name = "my-github-oidc-configuration"
priority = 1

claims_json = jsonencode({
"sub" = "repo:humpty/access-oidc-poc:ref:refs/heads/main",
"workflow_ref" = "humpty/access-oidc-poc/.github/workflows/job.yaml@refs/heads/main"
})

token_spec = {
groups_pattern = "{{group}}"
audience = "*@*"
expires_in = 7200
}
}
88 changes: 69 additions & 19 deletions pkg/platform/resource_oidc_identity_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,19 +90,48 @@ func (r *odicIdentityMappingResource) Schema(ctx context.Context, req resource.S
"username": schema.StringAttribute{
Optional: true,
Validators: []validator.String{
stringvalidator.AlsoRequires(
path.MatchRelative().AtParent().AtName("scope"),
),
stringvalidator.ConflictsWith(
path.MatchRelative().AtParent().AtName("username_pattern"),
path.MatchRelative().AtParent().AtName("groups_pattern"),
),
stringvalidator.LengthAtLeast(1),
},
Description: "User name of the OIDC user. Not applicable when `scope` is set to `applied-permissions/groups`. Must be set when `scope` is set to `applied-permissions/roles`.",
},
"username_pattern": schema.StringAttribute{
Optional: true,
Validators: []validator.String{
stringvalidator.ConflictsWith(
path.MatchRelative().AtParent().AtName("username"),
path.MatchRelative().AtParent().AtName("groups_pattern"),
),
stringvalidator.LengthAtLeast(1),
},
Description: "Provide a pattern which is used to map OIDC user to Artifactory user.",
},
"groups_pattern": schema.StringAttribute{
Optional: true,
Validators: []validator.String{
stringvalidator.ConflictsWith(
path.MatchRelative().AtParent().AtName("username"),
path.MatchRelative().AtParent().AtName("username_pattern"),
),
stringvalidator.LengthAtLeast(1),
},
Description: "Provide a pattern which is used to map OIDC groups to Artifactory groups.",
},
"scope": schema.StringAttribute{
Required: true,
Optional: true,
Validators: []validator.String{
stringvalidator.RegexMatches(
regexp.MustCompile(`^(applied-permissions\/admin|applied-permissions\/user|applied-permissions\/groups:.+|applied-permissions\/roles:.+)$`),
"must start with either 'applied-permissions/admin', 'applied-permissions/user', 'applied-permissions/groups:', or 'applied-permissions/roles:'",
),
},
MarkdownDescription: "Scope of the token. Must start with `applied-permissions/user`, `applied-permissions/admin`, `applied-permissions/roles:`, or `applied-permissions/groups:`. Group names must be comma-separated, double quotes wrapped, e.g. `applied-permissions/groups:\\\"readers\\\",\\\"my-group\\\",` Role permissions must be comma-separated, double quotes wrapped, e.g. `applied-permissions:roles:\"Developer\",\"Viewer\". `username` is also required when setting role permission.",
MarkdownDescription: "Scope of the token. Must start with `applied-permissions/user`, `applied-permissions/admin`, `applied-permissions/roles:`, or `applied-permissions/groups:`. Group names must be comma-separated, double quotes wrapped, e.g. `applied-permissions/groups:\\\"readers\\\",\\\"my-group\\\",` Role permissions are only applicable when in project scope and must be comma-separated, double quotes wrapped, e.g. `applied-permissions:roles:<project-key>:\"Developer\",\"Viewer\". `username` is also required when setting role permission.",
},
"audience": schema.StringAttribute{
Optional: true,
Expand Down Expand Up @@ -151,17 +180,21 @@ type odicIdentityMappingResourceModel struct {
}

type odicIdentityMappingTokenSpecResourceModel struct {
Username types.String `tfsdk:"username"`
Scope types.String `tfsdk:"scope"`
Audience types.String `tfsdk:"audience"`
ExpiresIn types.Int64 `tfsdk:"expires_in"`
Username types.String `tfsdk:"username"`
UsernamePattern types.String `tfsdk:"username_pattern"`
GroupsPattern types.String `tfsdk:"groups_pattern"`
Scope types.String `tfsdk:"scope"`
Audience types.String `tfsdk:"audience"`
ExpiresIn types.Int64 `tfsdk:"expires_in"`
}

var odicIdentityMappingTokenSpecResourceModelAttributeType map[string]attr.Type = map[string]attr.Type{
"username": types.StringType,
"scope": types.StringType,
"audience": types.StringType,
"expires_in": types.Int64Type,
"username": types.StringType,
"username_pattern": types.StringType,
"groups_pattern": types.StringType,
"scope": types.StringType,
"audience": types.StringType,
"expires_in": types.Int64Type,
}

func (r *odicIdentityMappingResourceModel) toAPIModel(ctx context.Context, apiModel *odicIdentityMappingAPIModel) (ds diag.Diagnostics) {
Expand All @@ -187,10 +220,12 @@ func (r *odicIdentityMappingResourceModel) toAPIModel(ctx context.Context, apiMo
Priority: r.Priority.ValueInt64(),
Claims: claims,
TokenSpec: odicIdentityMappingTokenSpecAPIModel{
Username: tokenSpec.Username.ValueString(),
Scope: tokenSpec.Scope.ValueString(),
Audience: tokenSpec.Audience.ValueString(),
ExpiresIn: tokenSpec.ExpiresIn.ValueInt64(),
Username: tokenSpec.Username.ValueString(),
UsernamePattern: tokenSpec.UsernamePattern.ValueString(),
GroupsPattern: tokenSpec.GroupsPattern.ValueString(),
Scope: tokenSpec.Scope.ValueString(),
Audience: tokenSpec.Audience.ValueString(),
ExpiresIn: tokenSpec.ExpiresIn.ValueInt64(),
},
ProjectKey: r.ProjectKey.ValueString(),
}
Expand Down Expand Up @@ -218,12 +253,25 @@ func (r *odicIdentityMappingResourceModel) fromAPIModel(ctx context.Context, api
r.ClaimsJSON = types.StringValue(string(claimsBytes))

tokenSpecResource := odicIdentityMappingTokenSpecResourceModel{
Scope: types.StringValue(apiModel.TokenSpec.Scope),
ExpiresIn: types.Int64Value(apiModel.TokenSpec.ExpiresIn),
}

if len(apiModel.TokenSpec.Username) > 0 {
tokenSpecResource.Username = types.StringValue(apiModel.TokenSpec.Username)
}

if len(apiModel.TokenSpec.UsernamePattern) > 0 {
tokenSpecResource.UsernamePattern = types.StringValue(apiModel.TokenSpec.UsernamePattern)
}

if len(apiModel.TokenSpec.GroupsPattern) > 0 {
tokenSpecResource.GroupsPattern = types.StringValue(apiModel.TokenSpec.GroupsPattern)
}

if len(apiModel.TokenSpec.Scope) > 0 {
tokenSpecResource.Scope = types.StringValue(apiModel.TokenSpec.Scope)
}

if len(apiModel.TokenSpec.Audience) > 0 {
tokenSpecResource.Audience = types.StringValue(apiModel.TokenSpec.Audience)
}
Expand Down Expand Up @@ -255,10 +303,12 @@ type odicIdentityMappingAPIModel struct {
}

type odicIdentityMappingTokenSpecAPIModel struct {
Username string `json:"username,omitempty"`
Scope string `json:"scope"`
Audience string `json:"audience"`
ExpiresIn int64 `json:"expires_in"`
Username string `json:"username,omitempty"`
UsernamePattern string `json:"username_pattern,omitempty"`
GroupsPattern string `json:"groups_pattern,omitempty"`
Scope string `json:"scope"`
Audience string `json:"audience"`
ExpiresIn int64 `json:"expires_in"`
}

func (r *odicIdentityMappingResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
Expand Down
Loading

0 comments on commit cd9a35b

Please sign in to comment.