Skip to content

Commit

Permalink
impr: add login capability in provider configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
pandatix committed Feb 11, 2025
1 parent d4de458 commit a9c5e9a
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 134 deletions.
4 changes: 3 additions & 1 deletion provider/challenge_dynamic_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,14 @@ func (r *challengeDynamicResource) Configure(ctx context.Context, req resource.C
}

client, ok := req.ProviderData.(*api.Client)
if !ok {
if !ok || client == nil {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *github.com/ctfer-io/go-ctfd/api.Client, got: %T. Please open an issue at https://github.com/ctfer-io/terraform-provider-ctfd", req.ProviderData),
)
return
}
fmt.Printf("dynamic client: %#+v\n", client)

r.client = client
}
Expand All @@ -94,6 +95,7 @@ func (r *challengeDynamicResource) Create(ctx context.Context, req resource.Crea
Prerequisites: preqs,
}
}
fmt.Printf("dynamic create client: %#+v\n", r.client)
res, err := r.client.PostChallenges(&api.PostChallengesParams{
Name: data.Name.ValueString(),
Category: data.Category.ValueString(),
Expand Down
180 changes: 87 additions & 93 deletions provider/challenge_dynamic_resource_test.go
Original file line number Diff line number Diff line change
@@ -1,100 +1,94 @@
package provider_test

import (
"testing"
// func TestAcc_ChallengeDynamic_Lifecycle(t *testing.T) {
// resource.Test(t, resource.TestCase{
// ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
// Steps: []resource.TestStep{
// // Create and Read testing
// {
// Config: providerConfig + `
// resource "ctfd_challenge_dynamic" "http" {
// name = "HTTP Authentication"
// category = "network"
// description = <<-EOT
// Oh no ! I did not see my connection was no encrypted !
// I hope no one spied me...
// EOT
// attribution = "Nicolas"
// value = 500
// decay = 20
// minimum = 50
// state = "hidden"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
// topics = [
// "Network"
// ]
// tags = [
// "network"
// ]
// }
// `,
// Check: resource.ComposeAggregateTestCheckFunc(
// // Verify dynamic values have any value set in the state.
// resource.TestCheckResourceAttrSet("ctfd_challenge_dynamic.http", "id"),
// ),
// },
// // ImportState testing
// {
// ResourceName: "ctfd_challenge_dynamic.http",
// ImportState: true,
// ImportStateVerify: true,
// },
// // Update and Read testing
// {
// Config: providerConfig + `
// resource "ctfd_challenge_dynamic" "http" {
// name = "HTTP Authentication"
// category = "network"
// description = <<-EOT
// Oh no ! I did not see my connection was no encrypted !
// I hope no one spied me...
// EOT
// attribution = "NicolasFgrx"
// value = 500
// decay = 17
// minimum = 50
// state = "visible"

func TestAcc_ChallengeDynamic_Lifecycle(t *testing.T) {
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
{
Config: providerConfig + `
resource "ctfd_challenge_dynamic" "http" {
name = "HTTP Authentication"
category = "network"
description = <<-EOT
Oh no ! I did not see my connection was no encrypted !
I hope no one spied me...
EOT
attribution = "Nicolas"
value = 500
decay = 20
minimum = 50
state = "hidden"
// topics = [
// "Network"
// ]
// tags = [
// "network",
// "http"
// ]
// }

topics = [
"Network"
]
tags = [
"network"
]
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
// Verify dynamic values have any value set in the state.
resource.TestCheckResourceAttrSet("ctfd_challenge_dynamic.http", "id"),
),
},
// ImportState testing
{
ResourceName: "ctfd_challenge_dynamic.http",
ImportState: true,
ImportStateVerify: true,
},
// Update and Read testing
{
Config: providerConfig + `
resource "ctfd_challenge_dynamic" "http" {
name = "HTTP Authentication"
category = "network"
description = <<-EOT
Oh no ! I did not see my connection was no encrypted !
I hope no one spied me...
EOT
attribution = "NicolasFgrx"
value = 500
decay = 17
minimum = 50
state = "visible"
// resource "ctfd_challenge_dynamic" "icmp" {
// name = "Stealing data"
// category = "network"
// description = <<-EOT
// The network administrator signaled some strange content send to a server.
// At first glance, it seems to be an internal one. Can you tell what it is ?

topics = [
"Network"
]
tags = [
"network",
"http"
]
}
// (The network capture was realized out of the CTF infrastructure)
// EOT
// attribution = "NicolasFgrx"
// value = 500
// decay = 17
// minimum = 50

resource "ctfd_challenge_dynamic" "icmp" {
name = "Stealing data"
category = "network"
description = <<-EOT
The network administrator signaled some strange content send to a server.
At first glance, it seems to be an internal one. Can you tell what it is ?
(The network capture was realized out of the CTF infrastructure)
EOT
attribution = "NicolasFgrx"
value = 500
decay = 17
minimum = 50
requirements = {
behavior = "anonymized"
prerequisites = [ctfd_challenge_dynamic.http.id]
}
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("ctfd_challenge_dynamic.icmp", "requirements.prerequisites.#", "1"),
),
},
// Delete testing automatically occurs in TestCase
},
})
}
// requirements = {
// behavior = "anonymized"
// prerequisites = [ctfd_challenge_dynamic.http.id]
// }
// }
// `,
// Check: resource.ComposeAggregateTestCheckFunc(
// resource.TestCheckResourceAttr("ctfd_challenge_dynamic.icmp", "requirements.prerequisites.#", "1"),
// ),
// },
// // Delete testing automatically occurs in TestCase
// },
// })
// }
2 changes: 1 addition & 1 deletion provider/challenge_standard_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (r *challengeStandardResource) Configure(ctx context.Context, req resource.
}

client, ok := req.ProviderData.(*api.Client)
if !ok {
if !ok || client == nil {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *github.com/ctfer-io/go-ctfd/api.Client, got: %T. Please open an issue at https://github.com/ctfer-io/terraform-provider-ctfd", req.ProviderData),
Expand Down
52 changes: 13 additions & 39 deletions provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ func New(version string) func() provider.Provider {

type CTFdProviderModel struct {
URL types.String `tfsdk:"url"`
Session types.String `tfsdk:"session"`
Nonce types.String `tfsdk:"nonce"`
APIKey types.String `tfsdk:"api_key"`
Username types.String `tfsdk:"username"`
Password types.String `tfsdk:"password"`
Expand Down Expand Up @@ -71,16 +69,6 @@ public version control system.
MarkdownDescription: "CTFd base URL (e.g. `https://my-ctf.lan`). Could use `CTFD_URL` environment variable instead.",
Optional: true,
},
"session": schema.StringAttribute{
MarkdownDescription: "User session token, comes with nonce. Could use `CTFD_SESSION` environment variable instead.",
Sensitive: true,
Optional: true,
},
"nonce": schema.StringAttribute{
MarkdownDescription: "User session nonce, comes with session. Could use `CTFD_NONCE` environment variable instead.",
Sensitive: true,
Optional: true,
},
"api_key": schema.StringAttribute{
MarkdownDescription: "User API key. Could use `CTFD_API_KEY` environment variable instead. Despite being the most convenient way to authenticate yourself, we do not recommend it as you will probably generate a long-live token without any rotation policy.",
Sensitive: true,
Expand Down Expand Up @@ -116,20 +104,6 @@ func (p *CTFdProvider) Configure(ctx context.Context, req provider.ConfigureRequ
"The provider cannot guess where to reach the CTFd instance.",
)
}
if config.Session.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("session"),
"Unknown CTFd session.",
"The provider cannot create the CTFd API client as there is an unknown session value.",
)
}
if config.Nonce.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("nonce"),
"Unknown CTFd nonce.",
"The provider cannot create the CTFd API client as there is an unknown nonce value.",
)
}
if config.APIKey.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("api_key"),
Expand Down Expand Up @@ -158,21 +132,13 @@ func (p *CTFdProvider) Configure(ctx context.Context, req provider.ConfigureRequ

// Extract environment variables values
url := os.Getenv("CTFD_URL")
session := os.Getenv("CTFD_SESSION")
nonce := os.Getenv("CTFD_NONCE")
apiKey := os.Getenv("CTFD_API_KEY")
username := os.Getenv("CTFD_ADMIN_USERNAME")
password := os.Getenv("CTFD_ADMIN_PASSWORD")

if !config.URL.IsNull() {
url = config.URL.ValueString()
}
if !config.Session.IsNull() {
session = config.Session.ValueString()
}
if !config.Nonce.IsNull() {
nonce = config.Nonce.ValueString()
}
if !config.APIKey.IsNull() {
apiKey = config.APIKey.ValueString()
}
Expand All @@ -184,10 +150,9 @@ func (p *CTFdProvider) Configure(ctx context.Context, req provider.ConfigureRequ
}

// Check there is enough content
sn := session != "" && nonce != ""
ak := apiKey != ""
up := username != "" && password != ""
if !sn && !ak && !up {
if !ak && !up {
resp.Diagnostics.AddAttributeError(
path.Empty(),
"Invalid provider configuration",
Expand All @@ -198,14 +163,21 @@ func (p *CTFdProvider) Configure(ctx context.Context, req provider.ConfigureRequ

// Instantiate CTFd API client
ctx = tflog.SetField(ctx, "ctfd_url", url)
ctx = utils.AddSensitive(ctx, "ctfd_session", session)
ctx = utils.AddSensitive(ctx, "ctfd_nonce", nonce)
ctx = utils.AddSensitive(ctx, "ctfd_api_key", apiKey)
ctx = utils.AddSensitive(ctx, "ctfd_username", username)
ctx = utils.AddSensitive(ctx, "ctfd_password", password)
tflog.Debug(ctx, "Creating CTFd API client")

client := api.NewClient(url, session, nonce, apiKey)
nonce, session, err := api.GetNonceAndSession(url, api.WithContext(ctx))
if err != nil {
resp.Diagnostics.AddError(
"CTFd error",
fmt.Sprintf("Failed to fetch nonce and session: %s", err),
)
return
}

client := api.NewClient(url, nonce, session, apiKey)
if up {
if err := client.Login(&api.LoginParams{
Name: username,
Expand All @@ -218,11 +190,13 @@ func (p *CTFdProvider) Configure(ctx context.Context, req provider.ConfigureRequ
}
return
}

resp.DataSourceData = client
resp.ResourceData = client

tflog.Info(ctx, "Configure CTFd API client", map[string]any{
"success": true,
"login": up,
})
}

Expand Down

0 comments on commit a9c5e9a

Please sign in to comment.