Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add platform_http_sso_settings resource #185

Merged
merged 5 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 2.1.0 (December 20, 2024). Tested on Artifactory 7.98.11 with Terraform 1.10.3 and OpenTofu 1.8.7

FEATURES:

**New Resource:**

* `platform_http_sso_settings` - Resource to manage HTTP SSO settings. PR: [#185](https://github.com/jfrog/terraform-provider-platform/pull/185)

## 2.0.0 (December 18, 2024). Tested on Artifactory 7.98.11 with Terraform 1.10.2 and OpenTofu 1.8.7

NOTES:
Expand Down
45 changes: 45 additions & 0 deletions docs/resources/http_sso_settings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "platform_http_sso_settings Resource - terraform-provider-platform"
subcategory: ""
description: |-
Provides a JFrog HTTP SSO Settings https://jfrog.com/help/r/jfrog-platform-administration-documentation/http-sso resource. This allows you to reuse existing HTTP-based SSO infrastructures with the JFrog Platform Unit (JPD), such as the SSO modules offered by Apache HTTPd.
---

# platform_http_sso_settings (Resource)

Provides a JFrog [HTTP SSO Settings](https://jfrog.com/help/r/jfrog-platform-administration-documentation/http-sso) resource. This allows you to reuse existing HTTP-based SSO infrastructures with the JFrog Platform Unit (JPD), such as the SSO modules offered by Apache HTTPd.

## Example Usage

```terraform
resource "platform_http_sso_settings" "my-http-sso-settings" {
proxied = true
auto_create_user = true
allow_user_to_access_profile = true
remote_user_request_variable = "MY_REMOTE_USER"
sync_ldap_groups = false
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `proxied` (Boolean) When set, Artifactory trusts incoming requests and reuses the remote user originally set on the request by the SSO of the HTTP server. This is useful if you want to use existing enterprise SSO integrations, such as the powerful authentication schemes provided by Apache (mod_auth_ldap, mod_auth_ntlm, mod_auth_kerb, etc.). When Artifactory is deployed as a webapp on Tomcat behind Apache: If using mod_jk, be sure to use the `JkEnvVar REMOTE_USER` directive in Apache's configuration.

### Optional

- `allow_user_to_access_profile` (Boolean) Auto created users will have access to their profile page and will be able to perform actions such as generating an API key. Default to `false`.
- `auto_create_user` (Boolean) When set, authenticated users are automatically created in Artifactory. When not set, for every request from an SSO user, the user is temporarily associated with default groups (if such groups are defined), and the permissions for these groups apply. Without automatic user creation, you must manually create the user inside Artifactory to manage user permissions not attached to their default groups. Default to `false`.
- `remote_user_request_variable` (String) The name of the HTTP request variable to use for extracting the user identity. Default to `REMOTE_USER`.
- `sync_ldap_groups` (Boolean) When set, the user will be associated with the groups returned in the LDAP login response. Note that the user's association with the returned groups is persistent if the `auto_create_user` is set. Default to `false`.

## Import

Import is supported using the following syntax:

```shell
terraform import platform_http_sso_settings.my-http-sso-settings my-http-sso-settings
```
1 change: 1 addition & 0 deletions examples/resources/platform_http_sso_settings/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
terraform import platform_http_sso_settings.my-http-sso-settings my-http-sso-settings
7 changes: 7 additions & 0 deletions examples/resources/platform_http_sso_settings/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resource "platform_http_sso_settings" "my-http-sso-settings" {
proxied = true
auto_create_user = true
allow_user_to_access_profile = true
remote_user_request_variable = "MY_REMOTE_USER"
sync_ldap_groups = false
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ require (
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.22.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down
1 change: 1 addition & 0 deletions pkg/platform/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ func (p *PlatformProvider) Resources(ctx context.Context) []func() resource.Reso
NewLicenseResource,
NewGlobalRoleResource,
NewGroupResource,
NewHTTPSSOSettingsResource,
NewOIDCConfigurationResource,
NewOIDCIdentityMappingResource,
NewMyJFrogIPAllowListResource,
Expand Down
237 changes: 237 additions & 0 deletions pkg/platform/resource_http_sso_settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
package platform

import (
"context"
"net/http"

"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/jfrog/terraform-provider-shared/util"
utilfw "github.com/jfrog/terraform-provider-shared/util/fw"
)

func NewHTTPSSOSettingsResource() resource.Resource {
return &HTTPSSOSettingsResource{
JFrogResource: util.JFrogResource{
TypeName: "platform_http_sso_settings",
ValidArtifactoryVersion: "7.57.1",
DocumentEndpoint: "access/api/v1/httpsso",
},
}
}

type HTTPSSOSettingsResource struct {
util.JFrogResource
}

type HTTPSSOSettingsResourceModel struct {
Proxied types.Bool `tfsdk:"proxied"`
AutoCreateUser types.Bool `tfsdk:"auto_create_user"`
AllowUserToAccessProfile types.Bool `tfsdk:"allow_user_to_access_profile"`
RemoteUserRequestVariable types.String `tfsdk:"remote_user_request_variable"`
SyncLDAPGroups types.Bool `tfsdk:"sync_ldap_groups"`
}

func (r *HTTPSSOSettingsResourceModel) toAPIModel(_ context.Context, apiModel *HTTPSSOSettingsAPIModel) diag.Diagnostics {
diags := diag.Diagnostics{}

apiModel.Proxied = r.Proxied.ValueBool()
apiModel.AutoCreateUser = r.AutoCreateUser.ValueBool()
apiModel.AllowUserToAccessProfile = r.AllowUserToAccessProfile.ValueBool()
apiModel.RemoteUserRequestVariable = r.RemoteUserRequestVariable.ValueString()
apiModel.SyncLDAPGroups = r.SyncLDAPGroups.ValueBool()

return diags
}

func (r *HTTPSSOSettingsResourceModel) fromAPIModel(_ context.Context, apiModel *HTTPSSOSettingsAPIModel) (ds diag.Diagnostics) {
r.Proxied = types.BoolValue(apiModel.Proxied)
r.AutoCreateUser = types.BoolValue(apiModel.AutoCreateUser)
r.AllowUserToAccessProfile = types.BoolValue(apiModel.AllowUserToAccessProfile)
r.RemoteUserRequestVariable = types.StringValue(apiModel.RemoteUserRequestVariable)
r.SyncLDAPGroups = types.BoolValue(apiModel.SyncLDAPGroups)

return
}

type HTTPSSOSettingsAPIModel struct {
Proxied bool `json:"http_sso_proxied"`
AutoCreateUser bool `json:"auto_create_user"`
AllowUserToAccessProfile bool `json:"allow_user_to_access_profile"`
RemoteUserRequestVariable string `json:"remote_user_request_variable"`
SyncLDAPGroups bool `json:"sync_ldap_groups"`
}

func (r *HTTPSSOSettingsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"proxied": schema.BoolAttribute{
Required: true,
MarkdownDescription: "When set, Artifactory trusts incoming requests and reuses the remote user originally set on the request by the SSO of the HTTP server. This is useful if you want to use existing enterprise SSO integrations, such as the powerful authentication schemes provided by Apache (mod_auth_ldap, mod_auth_ntlm, mod_auth_kerb, etc.). When Artifactory is deployed as a webapp on Tomcat behind Apache: If using mod_jk, be sure to use the `JkEnvVar REMOTE_USER` directive in Apache's configuration.",
},
"auto_create_user": schema.BoolAttribute{
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false),
MarkdownDescription: "When set, authenticated users are automatically created in Artifactory. When not set, for every request from an SSO user, the user is temporarily associated with default groups (if such groups are defined), and the permissions for these groups apply. Without automatic user creation, you must manually create the user inside Artifactory to manage user permissions not attached to their default groups. Default to `false`.",
},
"allow_user_to_access_profile": schema.BoolAttribute{
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false),
MarkdownDescription: "Auto created users will have access to their profile page and will be able to perform actions such as generating an API key. Default to `false`.",
},
"remote_user_request_variable": schema.StringAttribute{
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
Default: stringdefault.StaticString("REMOTE_USER"),
MarkdownDescription: "The name of the HTTP request variable to use for extracting the user identity. Default to `REMOTE_USER`.",
},
"sync_ldap_groups": schema.BoolAttribute{
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false),
MarkdownDescription: "When set, the user will be associated with the groups returned in the LDAP login response. Note that the user's association with the returned groups is persistent if the `auto_create_user` is set. Default to `false`.",
},
},
MarkdownDescription: "Provides a JFrog [HTTP SSO Settings](https://jfrog.com/help/r/jfrog-platform-administration-documentation/http-sso) resource. This allows you to reuse existing HTTP-based SSO infrastructures with the JFrog Platform Unit (JPD), such as the SSO modules offered by Apache HTTPd.",
}
}

func (r *HTTPSSOSettingsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
go util.SendUsageResourceCreate(ctx, r.ProviderData.Client.R(), r.ProviderData.ProductId, r.TypeName)

var plan HTTPSSOSettingsResourceModel

// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}

var httpSSOSettings HTTPSSOSettingsAPIModel
resp.Diagnostics.Append(plan.toAPIModel(ctx, &httpSSOSettings)...)
if resp.Diagnostics.HasError() {
return
}

var jfrogErrors util.JFrogErrors
response, err := r.ProviderData.Client.R().
SetBody(httpSSOSettings).
SetError(&jfrogErrors).
Put(r.DocumentEndpoint)

if err != nil {
utilfw.UnableToCreateResourceError(resp, err.Error())
return
}

if response.IsError() {
utilfw.UnableToCreateResourceError(resp, jfrogErrors.String())
return
}

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}

func (r *HTTPSSOSettingsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
go util.SendUsageResourceRead(ctx, r.ProviderData.Client.R(), r.ProviderData.ProductId, r.TypeName)

var state HTTPSSOSettingsResourceModel

resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

var httpSSOSettings HTTPSSOSettingsAPIModel

response, err := r.ProviderData.Client.R().
SetResult(&httpSSOSettings).
Get(r.DocumentEndpoint)

if err != nil {
utilfw.UnableToRefreshResourceError(resp, err.Error())
return
}

// Treat HTTP 404 Not Found status as a signal to recreate resource
// and return early
if response.StatusCode() == http.StatusNotFound {
resp.State.RemoveResource(ctx)
return
}

if response.IsError() {
utilfw.UnableToRefreshResourceError(resp, response.String())
return
}

// Convert from the API data model to the Terraform data model
// and refresh any attribute values.
resp.Diagnostics.Append(state.fromAPIModel(ctx, &httpSSOSettings)...)
if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (r *HTTPSSOSettingsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
go util.SendUsageResourceUpdate(ctx, r.ProviderData.Client.R(), r.ProviderData.ProductId, r.TypeName)

var plan HTTPSSOSettingsResourceModel

// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}

var httpSSOSettings HTTPSSOSettingsAPIModel
resp.Diagnostics.Append(plan.toAPIModel(ctx, &httpSSOSettings)...)
if resp.Diagnostics.HasError() {
return
}

response, err := r.ProviderData.Client.R().
SetBody(httpSSOSettings).
Put(r.DocumentEndpoint)

if err != nil {
utilfw.UnableToUpdateResourceError(resp, err.Error())
return
}

if response.IsError() {
utilfw.UnableToUpdateResourceError(resp, response.String())
return
}

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}

func (r *HTTPSSOSettingsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
go util.SendUsageResourceDelete(ctx, r.ProviderData.Client.R(), r.ProviderData.ProductId, r.TypeName)

resp.Diagnostics.AddWarning(
"Unable to Delete Resource",
"HTTP SSO settings cannot be deleted.",
)
}

func (r *HTTPSSOSettingsResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("remote_user_request_variable"), req, resp)
}
Loading
Loading