diff --git a/Makefile b/Makefile
index 55ddb1d..66f2829 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
.PHONY: test-acc
test-acc:
TF_ACC=1 \
- go test ./provider/ -v -run=^TestAcc_ -count=1 -coverprofile=cov.out -coverpkg "github.com/ctfer-io/terraform-provider-ctfd/provider,github.com/ctfer-io/terraform-provider-ctfd/provider/challenge,github.com/ctfer-io/terraform-provider-ctfd/provider/utils,github.com/ctfer-io/terraform-provider-ctfd/provider/validators"
+ go test ./provider/ -v -run=^TestAcc_ -count=1 -coverprofile=cov.out -coverpkg "github.com/ctfer-io/terraform-provider-ctfd/provider,github.com/ctfer-io/terraform-provider-ctfd/provider/utils,github.com/ctfer-io/terraform-provider-ctfd/provider/validators"
.PHONY: docs
docs:
diff --git a/docs/data-sources/challenges.md b/docs/data-sources/challenges.md
index 6a847c2..3d28791 100644
--- a/docs/data-sources/challenges.md
+++ b/docs/data-sources/challenges.md
@@ -29,10 +29,7 @@ Read-Only:
- `connection_info` (String) Connection Information to connect to the challenge instance, useful for pwn or web pentest.
- `decay` (Number)
- `description` (String) Description of the challenge, consider using multiline descriptions for better style.
-- `files` (Attributes List) List of files given to players to flag the challenge. (see [below for nested schema](#nestedatt--challenges--files))
-- `flags` (Attributes List) List of challenge flags that solves it. (see [below for nested schema](#nestedatt--challenges--flags))
- `function` (String) Decay function to define how the challenge value evolve through solves, either linear or logarithmic.
-- `hints` (Attributes List) List of hints about the challenge displayed to the end-user. (see [below for nested schema](#nestedatt--challenges--hints))
- `id` (String) Identifier of the challenge.
- `max_attempts` (Number) Maximum amount of attempts before being unable to flag the challenge.
- `minimum` (Number)
@@ -45,40 +42,6 @@ Read-Only:
- `type` (String) Type of the challenge defining its layout, either standard or dynamic.
- `value` (Number)
-
-### Nested Schema for `challenges.files`
-
-Read-Only:
-
-- `content` (String)
-- `contentb64` (String)
-- `id` (String)
-- `location` (String)
-- `name` (String)
-
-
-
-### Nested Schema for `challenges.flags`
-
-Read-Only:
-
-- `content` (String)
-- `data` (String)
-- `id` (String)
-- `type` (String)
-
-
-
-### Nested Schema for `challenges.hints`
-
-Read-Only:
-
-- `content` (String)
-- `cost` (Number)
-- `id` (String)
-- `requirements` (List of String)
-
-
### Nested Schema for `challenges.requirements`
diff --git a/docs/resources/challenge.md b/docs/resources/challenge.md
index 156b222..86ac1f0 100644
--- a/docs/resources/challenge.md
+++ b/docs/resources/challenge.md
@@ -26,10 +26,6 @@ resource "ctfd_challenge" "http" {
state = "visible"
function = "logarithmic"
- flags = [{
- content = "CTF{some_flag}"
- }]
-
topics = [
"Misc"
]
@@ -37,19 +33,30 @@ resource "ctfd_challenge" "http" {
"misc",
"basic"
]
+}
- hints = [{
- content = "Some super-helpful hint"
- cost = 50
- }, {
- content = "Even more helpful hint !"
- cost = 50
- }]
-
- files = [{
- name = "image.png"
- contentb64 = filebase64(".../image.png")
- }]
+resource "ctfd_flag" "http_flag" {
+ challenge_id = ctfd_challenge.http.id
+ content = "CTF{some_flag}"
+}
+
+resource "ctfd_hint" "http_hint_1" {
+ challenge_id = ctfd_challenge.http.id
+ content = "Some super-helpful hint"
+ cost = 50
+}
+
+resource "ctfd_hint" "http_hint_2" {
+ challenge_id = ctfd_challenge.http.id
+ content = "Even more helpful hint !"
+ cost = 50
+ requirements = [ctfd_hint.http_hint_1.id]
+}
+
+resource "ctfd_file" "http_file" {
+ challenge_id = ctfd_challenge.http.id
+ name = "image.png"
+ contentb64 = filebase64(".../image.png")
}
```
@@ -67,10 +74,7 @@ resource "ctfd_challenge" "http" {
- `connection_info` (String) Connection Information to connect to the challenge instance, useful for pwn, web and infrastructure pentests.
- `decay` (Number) The decay defines from each number of solves does the decay function triggers until reaching minimum. This function is defined by CTFd and could be configured through `.function`.
-- `files` (Attributes List) List of files given to players to flag the challenge. (see [below for nested schema](#nestedatt--files))
-- `flags` (Attributes List) List of challenge flags that solves it. (see [below for nested schema](#nestedatt--flags))
- `function` (String) Decay function to define how the challenge value evolve through solves, either linear or logarithmic.
-- `hints` (Attributes List) List of hints about the challenge displayed to the end-user. (see [below for nested schema](#nestedatt--hints))
- `max_attempts` (Number) Maximum amount of attempts before being unable to flag the challenge.
- `minimum` (Number) The minimum points for a dynamic-score challenge to reach with the decay function. Once there, no solve could have more value.
- `next` (Number) Suggestion for the end-user as next challenge to work on.
@@ -84,59 +88,6 @@ resource "ctfd_challenge" "http" {
- `id` (String) Identifier of the challenge.
-
-### Nested Schema for `files`
-
-Required:
-
-- `name` (String) Name of the file as displayed to end-users.
-
-Optional:
-
-- `content` (String, Sensitive) Raw content of the file, perfectly fit the use-cases of a .txt document or anything with a simple binary content. You could provide it from the file-system using `file("${path.module}/...")`.
-- `contentb64` (String, Sensitive) Base 64 content of the file, perfectly fit the use-cases of complex binaries. You could provide it from the file-system using `filebase64("${path.module}/...")`.
-- `location` (String) Location where the file is stored on the CTFd instance, for download purposes.
-
-Read-Only:
-
-- `id` (String) Identifier of the file, used internally to handle the CTFd corresponding object.
-- `sha1sum` (String) The sha1 sum of the file.
-
-
-
-### Nested Schema for `flags`
-
-Required:
-
-- `content` (String, Sensitive) The actual flag to match. Consider using the convention `MYCTF{value}` with `MYCTF` being the shortcode of your event's name and `value` depending on each challenge.
-
-Optional:
-
-- `data` (String) The flag sensitivity information, either case_sensitive or case_insensitive
-- `type` (String) The type of the flag, could be either static or regex
-
-Read-Only:
-
-- `id` (String) Identifier of the flag, used internally to handle the CTFd corresponding object.
-
-
-
-### Nested Schema for `hints`
-
-Required:
-
-- `content` (String) Content of the hint as displayed to the end-user.
-
-Optional:
-
-- `cost` (Number) Cost of the hint, and if any specified, the end-user will consume its own (or team) points to get it.
-- `requirements` (List of String) Other hints required to be consumed before getting this one. Useful for cost-increasing hint strategies with more and more help.
-
-Read-Only:
-
-- `id` (String) Identifier of the hint, used internally to handle the CTFd corresponding object.
-
-
### Nested Schema for `requirements`
diff --git a/docs/resources/file.md b/docs/resources/file.md
new file mode 100644
index 0000000..680608c
--- /dev/null
+++ b/docs/resources/file.md
@@ -0,0 +1,59 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "ctfd_file Resource - terraform-provider-ctfd"
+subcategory: ""
+description: |-
+ A CTFd file for a challenge.
+---
+
+# ctfd_file (Resource)
+
+A CTFd file for a challenge.
+
+## Example Usage
+
+```terraform
+resource "ctfd_challenge" "http" {
+ name = "My Challenge"
+ category = "misc"
+ description = "..."
+ value = 500
+ decay = 100
+ minimum = 50
+ state = "visible"
+ function = "logarithmic"
+
+ topics = [
+ "Misc"
+ ]
+ tags = [
+ "misc",
+ "basic"
+ ]
+}
+
+resource "ctfd_file" "http_file" {
+ challenge_id = ctfd_challenge.http.id
+ name = "image.png"
+ contentb64 = filebase64(".../image.png")
+}
+```
+
+
+## Schema
+
+### Required
+
+- `challenge_id` (String) Challenge of the file.
+- `name` (String) Name of the file as displayed to end-users.
+
+### Optional
+
+- `content` (String, Sensitive) Raw content of the file, perfectly fit the use-cases of a .txt document or anything with a simple binary content. You could provide it from the file-system using `file("${path.module}/...")`.
+- `contentb64` (String, Sensitive) Base 64 content of the file, perfectly fit the use-cases of complex binaries. You could provide it from the file-system using `filebase64("${path.module}/...")`.
+- `location` (String) Location where the file is stored on the CTFd instance, for download purposes.
+
+### Read-Only
+
+- `id` (String) Identifier of the file, used internally to handle the CTFd corresponding object. WARNING: updating this file does not work, requires full replacement.
+- `sha1sum` (String) The sha1 sum of the file.
diff --git a/docs/resources/flag.md b/docs/resources/flag.md
new file mode 100644
index 0000000..97a8077
--- /dev/null
+++ b/docs/resources/flag.md
@@ -0,0 +1,56 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "ctfd_flag Resource - terraform-provider-ctfd"
+subcategory: ""
+description: |-
+ A flag to solve the challenge.
+---
+
+# ctfd_flag (Resource)
+
+A flag to solve the challenge.
+
+## Example Usage
+
+```terraform
+resource "ctfd_challenge" "http" {
+ name = "My Challenge"
+ category = "misc"
+ description = "..."
+ value = 500
+ decay = 100
+ minimum = 50
+ state = "visible"
+ function = "logarithmic"
+
+ topics = [
+ "Misc"
+ ]
+ tags = [
+ "misc",
+ "basic"
+ ]
+}
+
+resource "ctfd_flag" "http_flag" {
+ challenge_id = ctfd_challenge.http.id
+ content = "CTF{some_flag}"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `challenge_id` (String) Challenge of the flag.
+- `content` (String, Sensitive) The actual flag to match. Consider using the convention `MYCTF{value}` with `MYCTF` being the shortcode of your event's name and `value` depending on each challenge.
+
+### Optional
+
+- `data` (String) The flag sensitivity information, either case_sensitive or case_insensitive
+- `type` (String) The type of the flag, could be either static or regex
+
+### Read-Only
+
+- `id` (String) Identifier of the flag, used internally to handle the CTFd corresponding object.
diff --git a/docs/resources/hint.md b/docs/resources/hint.md
new file mode 100644
index 0000000..b41fdf5
--- /dev/null
+++ b/docs/resources/hint.md
@@ -0,0 +1,69 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "ctfd_hint Resource - terraform-provider-ctfd"
+subcategory: ""
+description: |-
+ A hint for a challenge to help players solve it.
+---
+
+# ctfd_hint (Resource)
+
+A hint for a challenge to help players solve it.
+
+## Example Usage
+
+```terraform
+resource "ctfd_challenge" "http" {
+ name = "My Challenge"
+ category = "misc"
+ description = "..."
+ value = 500
+ decay = 100
+ minimum = 50
+ state = "visible"
+ function = "logarithmic"
+
+ topics = [
+ "Misc"
+ ]
+ tags = [
+ "misc",
+ "basic"
+ ]
+}
+
+resource "ctfd_flag" "http_flag" {
+ challenge_id = ctfd_challenge.http.id
+ content = "CTF{some_flag}"
+}
+
+resource "ctfd_hint" "http_hint" {
+ challenge_id = ctfd_challenge.http.id
+ content = "Some super-helpful hint"
+ cost = 50
+}
+
+resource "ctfd_hint" "http_hint_2" {
+ challenge_id = ctfd_challenge.http.id
+ content = "Even more helpful hint !"
+ cost = 50
+ requirements = [ctfd_hint.http_hint_1.id]
+}
+```
+
+
+## Schema
+
+### Required
+
+- `challenge_id` (String) Challenge of the hint.
+- `content` (String) Content of the hint as displayed to the end-user.
+
+### Optional
+
+- `cost` (Number) Cost of the hint, and if any specified, the end-user will consume its own (or team) points to get it.
+- `requirements` (List of String) List of the other hints it depends on.
+
+### Read-Only
+
+- `id` (String) Identifier of the hint, used internally to handle the CTFd corresponding object.
diff --git a/examples/provider-install-verification/main.tf b/examples/provider-install-verification/main.tf
index a812404..dbb1450 100644
--- a/examples/provider-install-verification/main.tf
+++ b/examples/provider-install-verification/main.tf
@@ -10,6 +10,8 @@ provider "ctfd" {
url = "http://localhost:8080"
}
+
+
resource "ctfd_challenge" "http" {
name = "HTTP Authentication"
category = "network"
@@ -27,10 +29,6 @@ resource "ctfd_challenge" "http" {
state = "visible"
function = "logarithmic"
- flags = [{
- content = "24HIUT{Http_1s_n0t_s3cuR3}"
- }]
-
topics = [
"Network"
]
@@ -38,21 +36,34 @@ resource "ctfd_challenge" "http" {
"network",
"http"
]
+}
- hints = [{
- content = "Les flux http ne sont pas chiffrés"
- cost = 50
- }, {
- content = "Les informations sont POSTées en HTTP :)"
- cost = 50
- }]
+resource "ctfd_flag" "http_flag" {
+ challenge_id = ctfd_challenge.http.id
+ content = "24HIUT{Http_1s_n0t_s3cuR3}"
+}
- files = [{
- name = "capture.pcapng"
- contentb64 = filebase64("${path.module}/capture.pcapng")
- }]
+resource "ctfd_hint" "http_hint_1" {
+ challenge_id = ctfd_challenge.http.id
+ content = "Les flux http ne sont pas chiffrés"
+ cost = 50
}
+resource "ctfd_hint" "http_hint_2" {
+ challenge_id = ctfd_challenge.http.id
+ content = "Les informations sont POSTées en HTTP :)"
+ cost = 50
+ requirements = [ctfd_hint.http_hint_1.id]
+}
+
+resource "ctfd_file" "http_file" {
+ challenge_id = ctfd_challenge.http.id
+ name = "capture.pcapng"
+ contentb64 = filebase64("${path.module}/capture.pcapng")
+}
+
+
+
resource "ctfd_challenge" "icmp" {
name = "Stealing data"
category = "network"
@@ -85,17 +96,28 @@ resource "ctfd_challenge" "icmp" {
"network",
"icmp"
]
+}
- hints = [{
- content = "Vous ne trouvez pas qu'il ya beaucoup de requêtes ICMP ?"
- cost = 50
- }, {
- content = "Pour l'exo, le ttl a été modifié, tente un `ip.ttl<=20`"
- cost = 50
- }]
+resource "ctfd_flag" "icmp_flag" {
+ challenge_id = ctfd_challenge.icmp.id
+ content = "24HIUT{IcmpExfiltrationIsEasy}"
+}
- files = [{
- name = "icmp.pcap"
- contentb64 = filebase64("${path.module}/icmp.pcap")
- }]
+resource "ctfd_hint" "icmp_hint_1" {
+ challenge_id = ctfd_challenge.icmp.id
+ content = "Vous ne trouvez pas qu'il ya beaucoup de requêtes ICMP ?"
+ cost = 50
+}
+
+resource "ctfd_hint" "icmp_hint_2" {
+ challenge_id = ctfd_challenge.icmp.id
+ content = "Pour l'exo, le ttl a été modifié, tente un `ip.ttl<=20`"
+ cost = 50
+ requirements = [ctfd_hint.icmp_hint_2.id]
+}
+
+resource "ctfd_file" "icmp_file" {
+ challenge_id = ctfd_challenge.icmp.id
+ name = "icmp.pcap"
+ contentb64 = filebase64("${path.module}/icmp.pcap")
}
diff --git a/examples/resources/ctfd_challenge/resource.tf b/examples/resources/ctfd_challenge/resource.tf
index d74d52b..e698b07 100644
--- a/examples/resources/ctfd_challenge/resource.tf
+++ b/examples/resources/ctfd_challenge/resource.tf
@@ -8,10 +8,6 @@ resource "ctfd_challenge" "http" {
state = "visible"
function = "logarithmic"
- flags = [{
- content = "CTF{some_flag}"
- }]
-
topics = [
"Misc"
]
@@ -19,17 +15,28 @@ resource "ctfd_challenge" "http" {
"misc",
"basic"
]
+}
+
+resource "ctfd_flag" "http_flag" {
+ challenge_id = ctfd_challenge.http.id
+ content = "CTF{some_flag}"
+}
- hints = [{
- content = "Some super-helpful hint"
- cost = 50
- }, {
- content = "Even more helpful hint !"
- cost = 50
- }]
+resource "ctfd_hint" "http_hint_1" {
+ challenge_id = ctfd_challenge.http.id
+ content = "Some super-helpful hint"
+ cost = 50
+}
+
+resource "ctfd_hint" "http_hint_2" {
+ challenge_id = ctfd_challenge.http.id
+ content = "Even more helpful hint !"
+ cost = 50
+ requirements = [ctfd_hint.http_hint_1.id]
+}
- files = [{
- name = "image.png"
- contentb64 = filebase64(".../image.png")
- }]
+resource "ctfd_file" "http_file" {
+ challenge_id = ctfd_challenge.http.id
+ name = "image.png"
+ contentb64 = filebase64(".../image.png")
}
diff --git a/examples/resources/ctfd_file/resource.tf b/examples/resources/ctfd_file/resource.tf
new file mode 100644
index 0000000..bf47e4e
--- /dev/null
+++ b/examples/resources/ctfd_file/resource.tf
@@ -0,0 +1,24 @@
+resource "ctfd_challenge" "http" {
+ name = "My Challenge"
+ category = "misc"
+ description = "..."
+ value = 500
+ decay = 100
+ minimum = 50
+ state = "visible"
+ function = "logarithmic"
+
+ topics = [
+ "Misc"
+ ]
+ tags = [
+ "misc",
+ "basic"
+ ]
+}
+
+resource "ctfd_file" "http_file" {
+ challenge_id = ctfd_challenge.http.id
+ name = "image.png"
+ contentb64 = filebase64(".../image.png")
+}
diff --git a/examples/resources/ctfd_flag/resource.tf b/examples/resources/ctfd_flag/resource.tf
new file mode 100644
index 0000000..4ca13f3
--- /dev/null
+++ b/examples/resources/ctfd_flag/resource.tf
@@ -0,0 +1,23 @@
+resource "ctfd_challenge" "http" {
+ name = "My Challenge"
+ category = "misc"
+ description = "..."
+ value = 500
+ decay = 100
+ minimum = 50
+ state = "visible"
+ function = "logarithmic"
+
+ topics = [
+ "Misc"
+ ]
+ tags = [
+ "misc",
+ "basic"
+ ]
+}
+
+resource "ctfd_flag" "http_flag" {
+ challenge_id = ctfd_challenge.http.id
+ content = "CTF{some_flag}"
+}
diff --git a/examples/resources/ctfd_hint/resource.tf b/examples/resources/ctfd_hint/resource.tf
new file mode 100644
index 0000000..94a1c6d
--- /dev/null
+++ b/examples/resources/ctfd_hint/resource.tf
@@ -0,0 +1,36 @@
+resource "ctfd_challenge" "http" {
+ name = "My Challenge"
+ category = "misc"
+ description = "..."
+ value = 500
+ decay = 100
+ minimum = 50
+ state = "visible"
+ function = "logarithmic"
+
+ topics = [
+ "Misc"
+ ]
+ tags = [
+ "misc",
+ "basic"
+ ]
+}
+
+resource "ctfd_flag" "http_flag" {
+ challenge_id = ctfd_challenge.http.id
+ content = "CTF{some_flag}"
+}
+
+resource "ctfd_hint" "http_hint" {
+ challenge_id = ctfd_challenge.http.id
+ content = "Some super-helpful hint"
+ cost = 50
+}
+
+resource "ctfd_hint" "http_hint_2" {
+ challenge_id = ctfd_challenge.http.id
+ content = "Even more helpful hint !"
+ cost = 50
+ requirements = [ctfd_hint.http_hint_1.id]
+}
diff --git a/go.mod b/go.mod
index 44242cb..0c79b23 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/ctfer-io/terraform-provider-ctfd
go 1.22
require (
- github.com/ctfer-io/go-ctfd v0.7.0
+ github.com/ctfer-io/go-ctfd v0.8.0
github.com/hashicorp/terraform-plugin-docs v0.19.2
github.com/hashicorp/terraform-plugin-framework v1.8.0
github.com/hashicorp/terraform-plugin-go v0.23.0
diff --git a/go.sum b/go.sum
index e32ead6..442804b 100644
--- a/go.sum
+++ b/go.sum
@@ -29,8 +29,8 @@ github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZ
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
-github.com/ctfer-io/go-ctfd v0.7.0 h1:/p8ynT3DTu201DANFIVc0otgE6eViSwcRuosyE9azaQ=
-github.com/ctfer-io/go-ctfd v0.7.0/go.mod h1:zOOgs1LmKEVW3rilcog0jT921vjShmR3avJbSMtvNyM=
+github.com/ctfer-io/go-ctfd v0.8.0 h1:as4TWW0OFI/4oZnJi8ad1pEeVqcZWVw2HiQl0rMDWNk=
+github.com/ctfer-io/go-ctfd v0.8.0/go.mod h1:zOOgs1LmKEVW3rilcog0jT921vjShmR3avJbSMtvNyM=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
diff --git a/provider/challenge/function.go b/provider/challenge/function.go
deleted file mode 100644
index 12ea7e7..0000000
--- a/provider/challenge/function.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// This file is related to the challenge.function attribute
-
-package challenge
-
-import "github.com/hashicorp/terraform-plugin-framework/types"
-
-var (
- FunctionLinear = types.StringValue("linear")
- FunctionLogarithmic = types.StringValue("logarithmic")
-)
diff --git a/provider/challenge/requirements_subresource.go b/provider/challenge/requirements_subresource.go
deleted file mode 100644
index 3698bd6..0000000
--- a/provider/challenge/requirements_subresource.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package challenge
-
-import (
- "github.com/ctfer-io/terraform-provider-ctfd/provider/utils"
- "github.com/hashicorp/terraform-plugin-framework/types"
-)
-
-var (
- BehaviorHidden = types.StringValue("hidden")
- BehaviorAnonymized = types.StringValue("anonymized")
-)
-
-type RequirementsSubresourceModel struct {
- Behavior types.String `tfsdk:"behavior"`
- Prerequisites []types.String `tfsdk:"prerequisites"`
-}
-
-func GetAnon(str types.String) *bool {
- switch {
- case str.Equal(BehaviorHidden):
- return nil
- case str.Equal(BehaviorAnonymized):
- return utils.Ptr(true)
- }
- panic("invalid anonymization value: " + str.ValueString())
-}
-
-func FromAnon(b *bool) types.String {
- if b == nil {
- return BehaviorHidden
- }
- if *b {
- return BehaviorAnonymized
- }
- panic("invalid anonymization value, got boolean false")
-}
diff --git a/provider/file_resource.go b/provider/file_resource.go
index 83fc182..afd01b5 100644
--- a/provider/file_resource.go
+++ b/provider/file_resource.go
@@ -49,7 +49,7 @@ func (r *fileResource) Metadata(ctx context.Context, req resource.MetadataReques
func (r *fileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
- MarkdownDescription: "",
+ MarkdownDescription: "A CTFd file for a challenge.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
@@ -61,22 +61,17 @@ func (r *fileResource) Schema(ctx context.Context, req resource.SchemaRequest, r
"challenge_id": schema.StringAttribute{
MarkdownDescription: "Challenge of the file.",
Required: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.RequiresReplace(),
- },
},
"name": schema.StringAttribute{
MarkdownDescription: "Name of the file as displayed to end-users.",
Required: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.RequiresReplace(),
- },
},
"location": schema.StringAttribute{
MarkdownDescription: "Location where the file is stored on the CTFd instance, for download purposes.",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
stringplanmodifier.RequiresReplace(),
},
},
@@ -84,7 +79,7 @@ func (r *fileResource) Schema(ctx context.Context, req resource.SchemaRequest, r
MarkdownDescription: "The sha1 sum of the file.",
Computed: true,
PlanModifiers: []planmodifier.String{
- stringplanmodifier.RequiresReplace(),
+ stringplanmodifier.UseStateForUnknown(),
},
},
"content": schema.StringAttribute{
@@ -93,6 +88,7 @@ func (r *fileResource) Schema(ctx context.Context, req resource.SchemaRequest, r
Computed: true,
Sensitive: true,
PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
stringplanmodifier.RequiresReplace(),
},
},
@@ -102,6 +98,7 @@ func (r *fileResource) Schema(ctx context.Context, req resource.SchemaRequest, r
Computed: true,
Sensitive: true,
PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
stringplanmodifier.RequiresReplace(),
},
},
@@ -191,7 +188,7 @@ func (r *fileResource) Read(ctx context.Context, req resource.ReadRequest, resp
data.Name = types.StringValue(filepath.Base(res.Location))
data.Location = types.StringValue(res.Location)
data.SHA1Sum = types.StringValue(res.SHA1sum)
- data.ChallengeID = lookForChallengeId(ctx, r.client, res.Location, resp.Diagnostics)
+ data.ChallengeID = lookForChallengeId(ctx, r.client, res.ID, resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}
@@ -267,14 +264,16 @@ func (data *fileResourceModel) PropagateContent(ctx context.Context, diags diag.
}
// XXX this helper only exist because CTFd does not return the challenge id of a file if it exist...
-func lookForChallengeId(ctx context.Context, client *api.Client, location string, diags diag.Diagnostics) types.String {
- challs, err := client.GetChallenges(nil, api.WithContext(ctx))
+func lookForChallengeId(ctx context.Context, client *api.Client, fileID int, diags diag.Diagnostics) types.String {
+ challs, err := client.GetChallenges(&api.GetChallengesParams{
+ View: utils.Ptr("admin"), // required, else CTFd only returns the "visible" challenges
+ }, api.WithContext(ctx))
if err != nil {
diags.AddError(
"CTFd Error",
fmt.Sprintf("Unable to query challenges, got error: %s", err),
)
- return types.StringValue("")
+ return types.StringNull()
}
for _, chall := range challs {
@@ -284,17 +283,17 @@ func lookForChallengeId(ctx context.Context, client *api.Client, location string
"CTFd Error",
fmt.Sprintf("Unable to query challenge %d files, got error: %s", chall.ID, err),
)
- return types.StringValue("")
+ return types.StringNull()
}
for _, file := range files {
- if file.Location == location {
+ if file.ID == fileID {
return types.StringValue(strconv.Itoa(chall.ID))
}
}
}
diags.AddError(
"Provider Error",
- fmt.Sprintf("Unable to find challenge of file at %s", location),
+ fmt.Sprintf("Unable to find challenge of file %d", fileID),
)
- return types.StringValue("")
+ return types.StringNull()
}
diff --git a/provider/file_resource_test.go b/provider/file_resource_test.go
index fe93641..4da8e4a 100644
--- a/provider/file_resource_test.go
+++ b/provider/file_resource_test.go
@@ -13,23 +13,19 @@ func TestAcc_File_Lifecycle(t *testing.T) {
// Create and Read testing
{
Config: providerConfig + `
-#resource "ctfd_challenge" "example" {
-# name = "Example challenge"
-# category = "test"
-# description = "Example challenge description..."
-# value = 500
-#}
+resource "ctfd_challenge" "example" {
+ name = "Example challenge"
+ category = "test"
+ description = "Example challenge description..."
+ value = 500
+}
resource "ctfd_file" "pouet" {
- challenge_id = "1"
+ challenge_id = ctfd_challenge.example.id
name = "pouet.txt"
content = "Pouet is a clown cat"
}
`,
- Check: resource.ComposeAggregateTestCheckFunc(
- // Verify dynamic values have any value set in the state.
- // resource.TestCheckResourceAttr("ctfd_flag.first", "requirements.#", "0"),
- ),
},
// ImportState testing
{
@@ -40,23 +36,19 @@ resource "ctfd_file" "pouet" {
// Update and Read testing
{
Config: providerConfig + `
-#resource "ctfd_challenge" "example" {
-# name = "Example challenge"
-# category = "test"
-# description = "Example challenge description..."
-# value = 500
-#}
+resource "ctfd_challenge" "example" {
+ name = "Example challenge"
+ category = "test"
+ description = "Example challenge description..."
+ value = 500
+}
resource "ctfd_file" "pouet" {
- challenge_id = "1"
+ challenge_id = ctfd_challenge.example.id
name = "pouet.txt"
content = "Pouet the 2nd is the clowniest cat ever"
}
`,
- Check: resource.ComposeAggregateTestCheckFunc(
- // resource.TestCheckResourceAttr("ctfd_flag.first", "requirements.#", "0"),
- // resource.TestCheckResourceAttr("ctfd_flag.second", "requirements.#", "1"),
- ),
},
// Delete testing automatically occurs in TestCase
},
diff --git a/provider/flag_resource.go b/provider/flag_resource.go
index d63a4b3..e588e80 100644
--- a/provider/flag_resource.go
+++ b/provider/flag_resource.go
@@ -48,7 +48,7 @@ func (r *flagResource) Metadata(ctx context.Context, req resource.MetadataReques
func (r *flagResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
- MarkdownDescription: "",
+ MarkdownDescription: "A flag to solve the challenge.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
MarkdownDescription: "Identifier of the flag, used internally to handle the CTFd corresponding object.",
diff --git a/provider/flag_resource_test.go b/provider/flag_resource_test.go
index 0a73101..7dc2526 100644
--- a/provider/flag_resource_test.go
+++ b/provider/flag_resource_test.go
@@ -26,10 +26,6 @@ resource "ctfd_flag" "static" {
type = "static"
}
`,
- Check: resource.ComposeAggregateTestCheckFunc(
- // Verify dynamic values have any value set in the state.
- // resource.TestCheckResourceAttr("ctfd_flag.first", "requirements.#", "0"),
- ),
},
// ImportState testing
{
@@ -60,10 +56,6 @@ resource "ctfd_flag" "regex" {
type = "regex"
}
`,
- Check: resource.ComposeAggregateTestCheckFunc(
- // resource.TestCheckResourceAttr("ctfd_flag.first", "requirements.#", "0"),
- // resource.TestCheckResourceAttr("ctfd_flag.second", "requirements.#", "1"),
- ),
},
// Delete testing automatically occurs in TestCase
},
diff --git a/provider/provider.go b/provider/provider.go
index 9fe5529..8b6ce67 100644
--- a/provider/provider.go
+++ b/provider/provider.go
@@ -5,6 +5,7 @@ import (
"os"
"github.com/ctfer-io/go-ctfd/api"
+ "github.com/ctfer-io/terraform-provider-ctfd/provider/utils"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/provider"
@@ -171,9 +172,9 @@ func (p *CTFdProvider) Configure(ctx context.Context, req provider.ConfigureRequ
// Instantiate CTFd API client
ctx = tflog.SetField(ctx, "ctfd_url", url)
- ctx = addSensitive(ctx, "ctfd_session", session)
- ctx = addSensitive(ctx, "ctfd_nonce", nonce)
- ctx = addSensitive(ctx, "ctfd_api_key", apiKey)
+ ctx = utils.AddSensitive(ctx, "ctfd_session", session)
+ ctx = utils.AddSensitive(ctx, "ctfd_nonce", nonce)
+ ctx = utils.AddSensitive(ctx, "ctfd_api_key", apiKey)
tflog.Debug(ctx, "Creating CTFd API client")
client := api.NewClient(url, session, nonce, apiKey)
diff --git a/provider/utils.go b/provider/utils.go
deleted file mode 100644
index 7a5036f..0000000
--- a/provider/utils.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package provider
-
-import (
- "context"
-
- "github.com/hashicorp/terraform-plugin-log/tflog"
-)
-
-func addSensitive(ctx context.Context, key string, value any) context.Context {
- ctx = tflog.SetField(ctx, key, value)
- return tflog.MaskFieldValuesWithFieldKeys(ctx, key)
-}
diff --git a/provider/utils/utils.go b/provider/utils/utils.go
index a64c29e..b16bdc9 100644
--- a/provider/utils/utils.go
+++ b/provider/utils/utils.go
@@ -1,12 +1,18 @@
package utils
import (
+ "context"
"strconv"
- "strings"
"github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
)
+func AddSensitive(ctx context.Context, key string, value any) context.Context {
+ ctx = tflog.SetField(ctx, key, value)
+ return tflog.MaskFieldValuesWithFieldKeys(ctx, key)
+}
+
// return a null types.Int64 if pointer is nil, else its value
func ToTFInt64(i *int) types.Int64 {
if i == nil {