From d97985edaa447558aeb0f87dcfc2ecf711e6b36f Mon Sep 17 00:00:00 2001 From: Joachim Halvorsen Seilen Date: Wed, 25 Sep 2024 12:08:24 +0200 Subject: [PATCH] Warning for userclient/machineclient if client has any nonapproved scopes --- internal/elvidapiclient/dtos.go | 2 + .../elvidapiclient/machineclientservice.go | 52 ++++++++----------- internal/elvidapiclient/userclientservice.go | 47 ++++++++--------- internal/provider/provider_shared.go | 4 ++ internal/provider/resource_machineclient.go | 19 ++++++- internal/provider/resource_userclient.go | 14 ++++- terraform-tester.tf | 8 +-- 7 files changed, 85 insertions(+), 61 deletions(-) diff --git a/internal/elvidapiclient/dtos.go b/internal/elvidapiclient/dtos.go index 3e05d66..b0cfad5 100644 --- a/internal/elvidapiclient/dtos.go +++ b/internal/elvidapiclient/dtos.go @@ -20,6 +20,7 @@ type UserClientDto struct { OneTimeUsageForRefreshTokens bool `json:"OneTimeUsageForRefreshTokens"` RefreshTokensLifeTime int `json:"RefreshTokensLifeTime"` ClientProperties []ClientPropertyDto `json:"ClientProperties"` + IsAllScopesApproved bool `json:"IsAllScopesApproved"` } type ClientPropertyDto struct { @@ -42,6 +43,7 @@ type MachineClientDto struct { AccessTokenLifeTime int `json:"AccessTokenLifeTime"` Scopes []string `json:"Scopes"` ClientClaims []ClientClaimDto `json:"ClientClaims"` + IsAllScopesApproved bool `json:"IsAllScopesApproved"` } type ClientClaimDto struct { diff --git a/internal/elvidapiclient/machineclientservice.go b/internal/elvidapiclient/machineclientservice.go index fe87ef5..0e0df94 100644 --- a/internal/elvidapiclient/machineclientservice.go +++ b/internal/elvidapiclient/machineclientservice.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "net/http" ) func CreateMachineClient(ctx context.Context, elvidAuthority string, accessTokenAD string, machineClientInput *MachineClientDto) (*MachineClientDto, error) { @@ -22,16 +23,7 @@ func CreateMachineClient(ctx context.Context, elvidAuthority string, accessToken return nil, ElvidErrorResponse(response, apiUrl) } - data, _ := io.ReadAll(response.Body) - defer response.Body.Close() - - var machineClient MachineClientDto - err = json.Unmarshal(data, &machineClient) - if err != nil { - return nil, err - } - - return &machineClient, nil + return responseAsMachineClientDto(response) } func ReadMachineClient(ctx context.Context, elvidAuthority string, accessTokenAD string, id string) (*MachineClientDto, error) { @@ -51,16 +43,7 @@ func ReadMachineClient(ctx context.Context, elvidAuthority string, accessTokenAD return nil, ElvidErrorResponse(response, apiUrl) } - data, _ := io.ReadAll(response.Body) - defer response.Body.Close() - - var machineClient MachineClientDto - err = json.Unmarshal(data, &machineClient) - if err != nil { - return nil, err - } - - return &machineClient, nil + return responseAsMachineClientDto(response) } func UpdateMachineClient(ctx context.Context, elvidAuthority string, accessTokenAD string, machineClient *MachineClientDto) (*MachineClientDto, error) { @@ -69,7 +52,6 @@ func UpdateMachineClient(ctx context.Context, elvidAuthority string, accessToken machineClientAsJson, _ := json.Marshal(machineClient) response, err := PatchRequest(ctx, apiUrl, accessTokenAD, machineClientAsJson) - if err != nil { return nil, err } @@ -78,16 +60,7 @@ func UpdateMachineClient(ctx context.Context, elvidAuthority string, accessToken return nil, ElvidErrorResponse(response, apiUrl) } - data, _ := io.ReadAll(response.Body) - defer response.Body.Close() - - var machineClientResponse MachineClientDto - err = json.Unmarshal(data, &machineClient) - if err != nil { - return nil, err - } - - return &machineClientResponse, nil + return responseAsMachineClientDto(response) } func DeleteMachineClient(ctx context.Context, elvidAuthority string, accessTokenAD string, id string) error { @@ -105,3 +78,20 @@ func DeleteMachineClient(ctx context.Context, elvidAuthority string, accessToken return nil } + +func responseAsMachineClientDto(response *http.Response) (*MachineClientDto, error) { + data, readErr := io.ReadAll(response.Body) + defer response.Body.Close() + + if readErr != nil { + return nil, readErr + } + + var machineClientResponseDto MachineClientDto + err := json.Unmarshal(data, &machineClientResponseDto) + if err != nil { + return nil, err + } + + return &machineClientResponseDto, nil +} diff --git a/internal/elvidapiclient/userclientservice.go b/internal/elvidapiclient/userclientservice.go index 2d87425..7b443c1 100644 --- a/internal/elvidapiclient/userclientservice.go +++ b/internal/elvidapiclient/userclientservice.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "net/http" ) func CreateUserClient(ctx context.Context, elvidAuthority string, accessTokenAD string, userClient *UserClientDto) (*UserClientDto, error) { @@ -20,16 +21,7 @@ func CreateUserClient(ctx context.Context, elvidAuthority string, accessTokenAD return nil, ElvidErrorResponse(response, apiUrl) } - data, _ := io.ReadAll(response.Body) - defer response.Body.Close() - - var createdUserClient UserClientDto - err = json.Unmarshal(data, &createdUserClient) - if err != nil { - return nil, err - } - - return &createdUserClient, nil + return responseAsUserClientDto(response) } func ReadUserClient(ctx context.Context, elvidAuthority string, accessTokenAD string, id string) (*UserClientDto, error) { @@ -49,33 +41,24 @@ func ReadUserClient(ctx context.Context, elvidAuthority string, accessTokenAD st return nil, ElvidErrorResponse(response, apiUrl) } - data, _ := io.ReadAll(response.Body) - defer response.Body.Close() - - var userClient UserClientDto - err = json.Unmarshal(data, &userClient) - if err != nil { - return nil, err - } - - return &userClient, nil + return responseAsUserClientDto(response) } -func UpdateUserClient(ctx context.Context, elvidAuthority string, accessTokenAD string, userClient *UserClientDto) error { +func UpdateUserClient(ctx context.Context, elvidAuthority string, accessTokenAD string, userClient *UserClientDto) (*UserClientDto, error) { apiUrl := fmt.Sprintf("%s/api/userclient", elvidAuthority) userClientAsJson, _ := json.Marshal(userClient) response, err := PatchRequest(ctx, apiUrl, accessTokenAD, userClientAsJson) if err != nil { - return err + return nil, err } if response.StatusCode != 200 { - return ElvidErrorResponse(response, apiUrl) + return nil, ElvidErrorResponse(response, apiUrl) } - return nil + return responseAsUserClientDto(response) } func DeleteUserClient(ctx context.Context, elvidAuthority string, accessTokenAD string, id string) error { @@ -93,3 +76,19 @@ func DeleteUserClient(ctx context.Context, elvidAuthority string, accessTokenAD return nil } + +func responseAsUserClientDto(response *http.Response) (*UserClientDto, error) { + data, readErr := io.ReadAll(response.Body) + defer response.Body.Close() + if readErr != nil { + return nil, readErr + } + + var userClientResponseDto UserClientDto + err := json.Unmarshal(data, &userClientResponseDto) + if err != nil { + return nil, err + } + + return &userClientResponseDto, nil +} diff --git a/internal/provider/provider_shared.go b/internal/provider/provider_shared.go index de2fa7f..2e39a50 100644 --- a/internal/provider/provider_shared.go +++ b/internal/provider/provider_shared.go @@ -38,3 +38,7 @@ func convertStringArrayToSet(array []string) types.Set { return set } + +func MissingScopeApprovalWarning(id string, clientName string) string { + return "Scope approval is missing for " + clientName + ". Please request scope approval in the #core-henvendelser channel on Slack. For more information about this client, visit: " + providerInput.ElvIDAuthority + "/Configuration/ClientDetails?client_Id=" + id +} diff --git a/internal/provider/resource_machineclient.go b/internal/provider/resource_machineclient.go index d801a8a..b2d9074 100644 --- a/internal/provider/resource_machineclient.go +++ b/internal/provider/resource_machineclient.go @@ -2,6 +2,7 @@ package provider import ( "context" + "encoding/json" "strconv" "github.com/3lvia/terraform-provider-elvid/internal/elvidapiclient" @@ -14,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" ) func MachineClientResourceSetup() resource.Resource { @@ -133,6 +135,10 @@ func (r *MachineClientResource) Create(ctx context.Context, req resource.CreateR plan.ClientId = types.StringValue(machineClientResponseDto.ClientId) plan.TokenEndpoint = types.StringValue(providerInput.ElvIDAuthority + "/connect/token") + if !machineClientResponseDto.IsAllScopesApproved { + resp.Diagnostics.AddWarning(MissingScopeApprovalWarning(strconv.Itoa(machineClientResponseDto.Id), machineClientResponseDto.ClientName), "During Create") + } + diags = resp.State.Set(ctx, plan) resp.Diagnostics.Append(diags...) } @@ -156,6 +162,10 @@ func (r *MachineClientResource) Read(ctx context.Context, req resource.ReadReque return } + if !machineClientResponseDto.IsAllScopesApproved { + resp.Diagnostics.AddWarning(MissingScopeApprovalWarning(state.Id.ValueString(), machineClientResponseDto.ClientName), "During Read") + } + state.MachineClientResourceFromDto(machineClientResponseDto) diags = resp.State.Set(ctx, state) resp.Diagnostics.Append(diags...) @@ -176,12 +186,19 @@ func (r *MachineClientResource) Update(ctx context.Context, req resource.UpdateR machineClientRequestDto.Id, _ = strconv.Atoi(plan.Id.ValueString()) - _, err := elvidapiclient.UpdateMachineClient(ctx, providerInput.ElvIDAuthority, providerInput.AccessTokenAD, machineClientRequestDto) + machineClientResponseDto, err := elvidapiclient.UpdateMachineClient(ctx, providerInput.ElvIDAuthority, providerInput.AccessTokenAD, machineClientRequestDto) if err != nil { resp.Diagnostics.AddError("Updating machineclient resulted in an error", err.Error()) return } + serialized, _ := json.Marshal(machineClientResponseDto) + tflog.Error(ctx, "During update machineClientResponseDto: "+string(serialized)) + + if !machineClientResponseDto.IsAllScopesApproved { + resp.Diagnostics.AddWarning(MissingScopeApprovalWarning(plan.Id.ValueString(), machineClientResponseDto.ClientName), "During Update") + } + plan.TokenEndpoint = types.StringValue(providerInput.ElvIDAuthority + "/connect/token") diags = resp.State.Set(ctx, plan) resp.Diagnostics.Append(diags...) diff --git a/internal/provider/resource_userclient.go b/internal/provider/resource_userclient.go index cea8c30..2c61f41 100644 --- a/internal/provider/resource_userclient.go +++ b/internal/provider/resource_userclient.go @@ -183,6 +183,10 @@ func (r *UserClientResource) Create(ctx context.Context, req resource.CreateRequ plan.Id = types.StringValue(strconv.Itoa(userClientResponseDto.Id)) plan.ClientId = types.StringValue(userClientResponseDto.ClientId) + if !userClientResponseDto.IsAllScopesApproved { + resp.Diagnostics.AddWarning(MissingScopeApprovalWarning(strconv.Itoa(userClientResponseDto.Id), userClientResponseDto.ClientName), "") + } + diags = resp.State.Set(ctx, plan) resp.Diagnostics.Append(diags...) } @@ -206,6 +210,10 @@ func (r *UserClientResource) Read(ctx context.Context, req resource.ReadRequest, return } + if !userClientResponseDto.IsAllScopesApproved { + resp.Diagnostics.AddWarning(MissingScopeApprovalWarning(state.Id.ValueString(), userClientResponseDto.ClientName), "") + } + state.UserClientResourceFromDto(userClientResponseDto) diags = resp.State.Set(ctx, state) resp.Diagnostics.Append(diags...) @@ -222,12 +230,16 @@ func (r *UserClientResource) Update(ctx context.Context, req resource.UpdateRequ userClientRequestDto := plan.DtoFromUserClientResource(diags) userClientRequestDto.Id, _ = strconv.Atoi(plan.Id.ValueString()) - err := elvidapiclient.UpdateUserClient(ctx, providerInput.ElvIDAuthority, providerInput.AccessTokenAD, userClientRequestDto) + userClientResponseDto, err := elvidapiclient.UpdateUserClient(ctx, providerInput.ElvIDAuthority, providerInput.AccessTokenAD, userClientRequestDto) if err != nil { resp.Diagnostics.AddError("Updating userclient resulted in an error", err.Error()) return } + if !userClientResponseDto.IsAllScopesApproved { + resp.Diagnostics.AddWarning(MissingScopeApprovalWarning(plan.Id.ValueString(), userClientResponseDto.ClientName), "") + } + diags = resp.State.Set(ctx, plan) resp.Diagnostics.Append(diags...) } diff --git a/terraform-tester.tf b/terraform-tester.tf index 3857718..95151b4 100644 --- a/terraform-tester.tf +++ b/terraform-tester.tf @@ -6,8 +6,8 @@ provider "elvid" { terraform_sp_client_id = var.terraform_sp_client_id terraform_sp_client_secret = var.terraform_sp_client_secret environment = var.environment - #override_elvid_authority = "https://localhost:44383" - override_elvid_authority = "https://elvid.dev-elvia.io" + override_elvid_authority = "https://localhost:44383" + #override_elvid_authority = "https://elvid.dev-elvia.io" run_hashed_secret_validation = true } @@ -55,9 +55,9 @@ provider "vault" { ## Machine client # resource "elvid_machineclient" "machineclient10" { -# name = "2024-09-16" +# name = "2024-09-23" # test_user_login_enabled = true -# access_token_life_time = 3512 +# access_token_life_time = 3522 # scopes = ["elvid.verifydeployment", "louvre.imageapi"] # resource_taint_version = "5" # # client_claims {