diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..f05340dd --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,14 @@ +### Definition of Ready + +- [ ] Short description of the feature/issue is added in the pr description +- [ ] PR is linked to the corresponding user story +- [ ] Acceptance criteria are met +- [ ] All open todos and follow ups are defined in a new ticket and justified +- [ ] Deviations from the acceptance criteria and design are agreed with the PO and documented. +- [ ] No debug or dead code +- [ ] My code has no repetitions +- [ ] All non-functional requirements are met +- [ ] The generic lifecycle acceptance test passes for affected resources. +- [ ] Examples are up-to-date and meaningful. The provider version is incremented. +- [ ] Docs are generated. +- [ ] Code is generated where possible. diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml new file mode 100644 index 00000000..b6df7d49 --- /dev/null +++ b/.github/workflows/pull_request.yaml @@ -0,0 +1,93 @@ +name: Test Provider + +on: pull_request + +jobs: + + lint: + + runs-on: ubuntu-20.04 + + permissions: + contents: read + + steps: + + - name: Checkout Code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version-file: 'go.mod' + cache: false + + - name: Avoid Typecheck Error On Embedded Fields + run: | + touch acceptance/keys/instance-level-admin-sa.json + touch acceptance/keys/org-level-admin-sa.json + + - name: Lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.53.3 + github-token: ${{ github.token }} + only-new-issues: true + skip-pkg-cache: true + skip-build-cache: true + + test: + + runs-on: ubuntu-20.04 + + permissions: + contents: read + + steps: + + - name: Checkout Code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version-file: 'go.mod' + + - name: Make Machinekey Directory Writable + working-directory: acceptance + run: "chmod -R 777 keys" + + - name: Setup ZITADEL + working-directory: acceptance + run: docker compose run setup + + - name: Download Go Modules + run: go mod download + + - name: Run Acceptance Tests + run: TF_ACC=1 go test -coverprofile=profile.cov ./... + + - name: Publish Coverage + uses: codecov/codecov-action@v3.1.4 + with: + file: profile.cov + name: acceptance-tests + flags: acceptance-tests + + - name: Save Docker Compose Logs + if: always() + working-directory: acceptance + run: docker compose logs > .docker-compose.log + + - name: Archive Docker Compose Logs + if: always() + uses: actions/upload-artifact@v3 + with: + name: pull-request-tests + path: | + acceptance/.docker-compose.log + retention-days: 30 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..be760909 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,45 @@ +# This GitHub action can publish assets for release when a tag is created. +# Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0). +# +# This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your +# private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE` +# secret. If you would rather own your own GPG handling, please fork this action +# or use an alternative one for key handling. +# +# You will need to pass the `--batch` flag to `gpg` in your signing step +# in `goreleaser` to indicate this is being used in a non-interactive mode. +# +name: release +on: + push: + tags: + - 'v*' +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Unshallow + run: git fetch --prune --unshallow + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version-file: 'go.mod' + cache: true + - name: Import GPG key + id: import_gpg + #https://github.com/hashicorp/ghaction-import-gpg/issues/11 + uses: crazy-max/ghaction-import-gpg@v5.0.0 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v3.0.0 + with: + version: latest + args: release --rm-dist + env: + GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} + # GitHub sets this automatically + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 7a3e2fd0..2200f7d0 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,12 @@ override.tf.json # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan # example: *tfplan* + +/test + +# IDEs +.idea +.run + +# local binary +terraform-provider-zitadel diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..798d30ab --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,13 @@ +run: + timeout: 10m +linters: + disable-all: true + enable: + - typecheck + - gci +linters-settings: + gci: + sections: + - standard + - default + - prefix(github.com/zitadel/terraform-provider-zitadel) diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 00000000..a5a2af13 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,60 @@ +# Visit https://goreleaser.com for documentation on how to customize this +# behavior. +before: + hooks: + # this is just an example and not a requirement for provider building/publishing + - go mod tidy +builds: + - env: + # goreleaser does not work with CGO, it could also complicate + # usage by users in CI/CD systems like Terraform Cloud where + # they are unable to install libraries. + - CGO_ENABLED=0 + mod_timestamp: '{{ .CommitTimestamp }}' + flags: + - -trimpath + ldflags: + - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' + goos: + - freebsd + - windows + - linux + - darwin + goarch: + - amd64 + - '386' + - arm + - arm64 + ignore: + - goos: darwin + goarch: '386' + binary: '{{ .ProjectName }}_v{{ .Version }}' +archives: + - format: zip + name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' +checksum: + extra_files: + - glob: 'terraform-registry-manifest.json' + name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' + name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' + algorithm: sha256 +signs: + - artifacts: checksum + args: + # if you are using this in a GitHub action or some other automated pipeline, you + # need to pass the batch flag to indicate its not interactive. + - "--batch" + - "--local-user" + - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key + - "--output" + - "${signature}" + - "--detach-sign" + - "${artifact}" +release: + extra_files: + - glob: 'terraform-registry-manifest.json' + name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' + # If you want to manually examine the release before its live, uncomment this line: + draft: true +changelog: + skip: true \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b00f7fb9..1d9a3758 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,48 @@ -# Git Base Branch +# Debug -Please use the alpha branch as base until we merge alpha to main. -You can find [more contributing instructions on the alpha branch](https://github.com/zitadel/terraform-provider-zitadel/blob/alpha/CONTRIBUTING.md). +1. Run the local plugin code with your debugging IDE of choice with something similar to `go run ./... -debug`. +2. Set breakpoints in your IDE. +3. In your shell, apply the resource you are working on. + ```bash + # export the printed environment variable from the go run ./... -debug command above. E.g. + export TF_REATTACH_PROVIDERS='{"registry.terraform.io/zitadel/zitadel":{"Protocol":"grpc","ProtocolVersion":6,"Pid":8123,"Test":true,"Addr":{"Network":"unix","String":"/tmp/plugin275634719"}}}' + # go to a directory containing .tf files. + cd /my-zitadel-terraform-files + + # apply them + terraform apply + ``` +4. The execution stops at your breakpoints. + +# Run Acceptance Tests + +Run a local ZITADEL instance using docker compose. + +```bash +# To have the machine key written with the correct ownership, set your current users ID. +export ZITADEL_DEV_UID="$(id -u)" + +# Setup ZITADEL +docker compose --file ./acceptance/docker-compose.yaml run setup +``` + +Run the accepance tests using the machine key generated by ZITADEL. + +```bash +TF_ACC=1 go test ./... +``` + +# Ensure the code is formatted correctly + +```bash +go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.3 run --fix +``` + +# Generate Docs + +1. Manually maintain files in /templates and /examples +1. Generate the docs +```bash +go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@v0.14.1 generate + ``` diff --git a/README.md b/README.md index cac294b0..071af69d 100644 --- a/README.md +++ b/README.md @@ -10,29 +10,17 @@ Learn more about ZITADEL by checking out the [source repository on GitHub](https ## About this Terraform Provider This is the official ZITADEL Terraform provider. -It lets you declare ZITADEL resources declaratively and apply the resources to any ZITADEL instance. +It lets you declare ZITADEL resources and apply the resources to any ZITADEL instance. Importing existing resources to the Terraform state is supported. -Therefore, as long as you have all resources declared, it is even easy to migrate state between instances. - -The provider is currently in [Beta state](https://zitadel.com/docs/support/general) and the support level is Enterprise Support. - -For general availability the following issues need to be resolved - -- [ ] [Release 1.0.0](https://github.com/zitadel/terraform-provider-zitadel/issues/85) -- [x] [Run Tests on PR](https://github.com/zitadel/terraform-provider-zitadel/issues/92) -- [ ] [Lifecycle Tests on all Ressources](https://github.com/zitadel/terraform-provider-zitadel/issues/91) ## Usage [Follow the Guide in our Docs](https://zitadel.com/docs/guides/manage/terraform/basics). Note that you need to create an authorized service user to access the ZITADEL APIs through the provider, as noted in the prerequisites. -We don't guarantee that all resources are available in the provider. -In case you miss something you are welcome to [contribute](#contributing). - ## Contributing -If you found a bug or want to request a new feature, please open an [issue](https://github.com/zitadel/terraform-provider-zitadel/issues). +If you find a bug or want to request a new feature, please open an [issue](https://github.com/zitadel/terraform-provider-zitadel/issues). Contributions to the provider are very welcome, please follow the general guidance in the [Contribution Guide](https://github.com/zitadel/terraform-provider-zitadel/blob/main/CONTRIBUTING.md). ## Contributors diff --git a/acceptance/Dockerfile b/acceptance/Dockerfile new file mode 100644 index 00000000..c2c3fc64 --- /dev/null +++ b/acceptance/Dockerfile @@ -0,0 +1,7 @@ +FROM golang:1.20-alpine +RUN apk add curl jq +RUN go install github.com/zitadel/zitadel-tools@v0.4.0 +COPY setup.sh config.json / +RUN chmod +x /setup.sh +WORKDIR / +ENTRYPOINT [ "/setup.sh" ] diff --git a/acceptance/config.go b/acceptance/config.go new file mode 100644 index 00000000..ef7cb70f --- /dev/null +++ b/acceptance/config.go @@ -0,0 +1,38 @@ +package acceptance + +import ( + _ "embed" + "encoding/json" +) + +var ( + //go:embed keys/instance-level-admin-sa.json + instanceLevelAdminSAJSON []byte + + //go:embed keys/org-level-admin-sa.json + orgLevelAdminSAJSON []byte + + //go:embed config.json + configJson []byte +) + +type Config struct { + OrgLevel IsolatedInstance + InstanceLevel IsolatedInstance +} + +type IsolatedInstance struct { + Domain string + AdminSAJSON []byte +} + +func GetConfig() Config { + val := Config{ + OrgLevel: IsolatedInstance{AdminSAJSON: orgLevelAdminSAJSON}, + InstanceLevel: IsolatedInstance{AdminSAJSON: instanceLevelAdminSAJSON}, + } + if err := json.Unmarshal(configJson, &val); err != nil { + panic(err) + } + return val +} diff --git a/acceptance/config.json b/acceptance/config.json new file mode 100644 index 00000000..87fe6d14 --- /dev/null +++ b/acceptance/config.json @@ -0,0 +1,8 @@ +{ + "orgLevel": { + "domain": "org-level-tests.default.127.0.0.1.sslip.io" + }, + "instanceLevel": { + "domain": "instance-level-tests.default.127.0.0.1.sslip.io" + } +} diff --git a/acceptance/docker-compose.yaml b/acceptance/docker-compose.yaml new file mode 100644 index 00000000..55fb67b9 --- /dev/null +++ b/acceptance/docker-compose.yaml @@ -0,0 +1,45 @@ +version: '3.8' + +services: + db: + image: 'cockroachdb/cockroach:v22.2.2' + command: 'start-single-node --insecure --http-addr :9090' + healthcheck: + test: ['CMD', 'curl', '-f', 'http://localhost:9090/health?ready=1'] + interval: '2s' + timeout: '30s' + retries: 5 + ports: + - "26257:26257" + - "9090:9090" + + zitadel: + user: '${ZITADEL_DEV_UID}' + image: '${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:v2.32.1}' + command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled --config /zitadel.yaml --steps /zitadel.yaml' + ports: + - "8080:8080" + volumes: + - ./keys:/keys + - ./zitadel.yaml:/zitadel.yaml + healthcheck: + test: ['CMD', '/app/zitadel', 'ready'] + interval: '2s' + timeout: '30s' + retries: 5 + depends_on: + db: + condition: 'service_healthy' + + setup: + user: '${ZITADEL_DEV_UID}' + container_name: setup + build: . + environment: + KEYS_DIRECTORY: /keys + SERVICE: http://zitadel:8080 + volumes: + - "./keys:/keys" + depends_on: + zitadel: + condition: 'service_healthy' diff --git a/acceptance/keys/.gitignore b/acceptance/keys/.gitignore new file mode 100644 index 00000000..926b5abd --- /dev/null +++ b/acceptance/keys/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!system-api-sa.pem diff --git a/acceptance/keys/system-api-sa.pem b/acceptance/keys/system-api-sa.pem new file mode 100644 index 00000000..2c7a6e7f --- /dev/null +++ b/acceptance/keys/system-api-sa.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAzi+FFSJL7f5yw4KTwzgMP34ePGycm/M+kT0M7V4Cgx5V3EaD +IvTQKTLfBaEB45zb9LtjIXzDw0rXRoS2hO6th+CYQCz3KCvh09C0IzxZiB2IS3H/ +aT+5Bx9EFY+vnAkZjccbyG5YNRvmtOlnvIeIH7qZ0tEwkPfF5GEZNPJPtmy3UGV7 +iofdVQS1xRj73+aMw5rvH4D8IdyiAC3VekIbpt0Vj0SUX3DwKtog337BzTiPk3aX +RF0sbFhQoqdJRI8NqgZjCwjq9yfI5tyxYswn+JGzHGdHvW3idODlmwEt5K2pasiR +IWK2OGfq+w0EcltQHabuqEPgZlmhCkRdNfixBwIDAQABAoIBAA9jNoBkRdxmH/R9 +Wz+3gBqA9Aq4ZFuzJJk8QCm62V8ltWyyCnliYeKhPEm0QWrWOwghr/1AzW9Wt4g4 +wVJcabD5TwODF5L0626eZcM3bsscwR44TMJzEgD5EWC2j3mKqFCPaoBj08tq4KXh +wW8tgjgz+eTk3cYD583qfTIZX1+SzSMBpetTBsssQtGhhOB/xPiuL7hi+fXmV2rh +8mc9X6+wJ5u3zepsyK0vBeEDmurD4ZUIXFrZ0WCB/wNkSW9VKyoH+RC1asQAgqTz +glJ/NPbDJSKGvSBQydoKkqoXx7MVJ8VObFddfgo4dtOoz6YCfUVBHt8qy+E5rz5y +CICjL/kCgYEA9MnHntVVKNXtEFZPo02xgCwS3eG27ZwjYgJ1ZkCHM5BuL4MS7qbr +743/POs1Ctaok0udHl1PFB4uAG0URnmkUnWzcoJYb6Plv03F0LRdsnfuhehfIxLP +nWvxSm5n21H4ytfxm0BWY09JkLDnJZtXrgTILbuqb9Wy6TmAvUaF2YUCgYEA16Ec +ywSaLVdqPaVpsTxi7XpRJAB2Isjp6RffNEecta4S0LL7s/IO3QXDH9SYpgmgCTah +3aXhpT4hIFlpg3eBjVfbOwgqub8DgirnSQyQt99edUtHIK+K8nMdGxz6X6pfTKzK +asSH7qPlt5tz1621vC0ocXSZR7zm99/FgwILwBsCgYBOsP8nJFV4By1qbxSy3qsN +FR4LjiAMSoFlZHzxHhVYkjmZtH1FkwuNuwwuPT6T+WW/1DLyK/Tb9se7A1XdQgV9 +LLE/Qn/Dg+C7mvjYmuL0GHHpQkYzNDzh0m2DC/L/Il7kdn8I9anPyxFPHk9wW3vY +SVlAum+T/BLDvuSP9DfbMQKBgCc1j7PG8XYfOB1fj7l/volqPYjrYI/wssAE7Dxo +bTGIJrm2YhiVgmhkXNfT47IFfAlQ2twgBsjyZDmqqIoUWAVonV+9m29NMYkg3g+l +bkdRIa74ckWaRgzSK8+7VDfDFjMuFFyXwhP9z460gLsORkaie4Et75Vg3yrhkNvC +qnpTAoGBAMguDSWBbCewXnHlKGFpm+LH+OIvVKGEhtCSvfZojtNrg/JBeBebSL1n +mmT1cONO+0O5bz7uVaRd3JdnH2JFevY698zFfhVsjVCrm+fz31i5cxAgC39G2Lfl +YkTaa1AFLstnf348ZjuvBN3USUYZo3X3mxnS+uluVuRSGwIKsN0a +-----END RSA PRIVATE KEY----- diff --git a/acceptance/setup.sh b/acceptance/setup.sh new file mode 100755 index 00000000..5404f03e --- /dev/null +++ b/acceptance/setup.sh @@ -0,0 +1,221 @@ +#!/bin/sh + +set -e + +############## +### CONFIG ### +############## + +KEYS_DIRECTORY=${KEYS_DIRECTORY:-./keys} + +if [ ! -d "${KEYS_DIRECTORY}" ]; then + echo "Directory ${KEYS_DIRECTORY} does not exist." + exit 1 +fi + +if [ ! -f "./config.json" ]; then + echo "File ./config.json does not exist." + exit 1 +fi + +if [ ! -f "${KEYS_DIRECTORY}/system-api-sa.pem" ]; then + echo "File ${KEYS_DIRECTORY}/system-api-sa.pem does not exist." + exit 1 +fi + +if [ ! -f "${KEYS_DIRECTORY}/org-level-admin-sa.json" ]; then + echo "File ${KEYS_DIRECTORY}/org-level-admin-sa.json does not exist." + echo "Did ZITADEL set up correctly?" + exit 1 +fi + +SYSTEM_API_PEM_KEY=${KEYS_DIRECTORY}/system-api-sa.pem +echo "Using path ${SYSTEM_API_PEM_KEY} to read the system api service account pem key from ." + +ORG_LEVEL_DOMAIN=$(cat ./config.json | jq --raw-output '.orgLevel.domain') +INSTANCE_LEVEL_DOMAIN=$(cat ./config.json | jq --raw-output '.instanceLevel.domain') + +ORG_LEVEL_KEY=${KEYS_DIRECTORY}/org-level-admin-sa.json +echo "Using path ${ORG_LEVEL_KEY} to read the ${ORG_LEVEL_DOMAIN} instances admin service account key from." + +INSTANCE_LEVEL_KEY=${KEYS_DIRECTORY}/instance-level-admin-sa.json +echo "Using path ${INSTANCE_LEVEL_KEY} to write the ${INSTANCE_LEVEL_DOMAIN} instances admin service account key to." + +AUDIENCE=${AUDIENCE:-http://$ORG_LEVEL_DOMAIN:8080} +echo "Using audience ${AUDIENCE} for which the key is used." + +SERVICE=${SERVICE:-$AUDIENCE} +echo "Using the service ${SERVICE} to connect to ZITADEL. For example in docker compose this can differ from the audience." + +###################################### +### CREATE INSTANCE LEVEL INSTANCE ### +###################################### + +echo "Creating SA_JWT for system API user" +SYSTEM_API_TOKEN=$(zitadel-tools key2jwt --key ${SYSTEM_API_PEM_KEY} --audience ${AUDIENCE} --issuer "system-api-sa") + +INSTANCE_LEVEL_INSTANCE_PAYLOAD=$(cat < ${INSTANCE_LEVEL_KEY} +fi + +######################### +### HUMAN USERS ######### +######################### + +create_human_admin () { + MGMT_AUDIENCE=$1 + MGMT_JSON_KEY=$2 + + echo "Creating human admin for ${MGMT_AUDIENCE}" + + AUDIENCE_HOST="$(echo $MGMT_AUDIENCE | cut -d/ -f3)" + echo "Deferred the Host header ${AUDIENCE_HOST} which will be sent in requests that ZITADEL then maps to a virtual instance" + + SA_JWT=$(zitadel-tools key2jwt --key ${MGMT_JSON_KEY} --audience ${MGMT_AUDIENCE}) + echo "Created JWT from Admin service account key ${SA_JWT}" + + TOKEN_RESPONSE=$(curl -s --request POST \ + --url ${SERVICE}/oauth/v2/token \ + --header 'Content-Type: application/x-www-form-urlencoded' \ + --header "Host: ${AUDIENCE_HOST}" \ + --data grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \ + --data scope='openid profile email urn:zitadel:iam:org:project:id:zitadel:aud' \ + --data assertion="${SA_JWT}") + echo "Got response from token endpoint:" + echo "${TOKEN_RESPONSE}" | jq + + TOKEN=$(echo ${TOKEN_RESPONSE} | jq --raw-output '.access_token') + echo "Extracted access token ${TOKEN}" + + ORG_RESPONSE=$(curl -s --request GET \ + --url ${SERVICE}/admin/v1/orgs/default \ + --header 'Accept: application/json' \ + --header "Authorization: Bearer ${TOKEN}" \ + --header "Host: ${AUDIENCE_HOST}") + echo "Got default org response:" + echo "${ORG_RESPONSE}" | jq + + ORG_ID=$(echo ${ORG_RESPONSE} | jq --raw-output '.org.id') + echo "Extracted default org id ${ORG_ID}" + + HUMAN_USER_USERNAME="zitadel-admin@zitadel.localhost" + HUMAN_USER_PASSWORD="Password1!" + + HUMAN_USER_PAYLOAD=$(cat < +## Schema + +### Required + +- `action_id` (String) The ID of this resource. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `allowed_to_fail` (Boolean) when true, the next action will be called even if this action fails +- `id` (String) The ID of this resource. +- `name` (String) +- `script` (String) +- `state` (Number) the state of the action +- `timeout` (String) after which time the action will be terminated if not finished \ No newline at end of file diff --git a/docs/data-sources/application_api.md b/docs/data-sources/application_api.md new file mode 100644 index 00000000..8b0a6cb3 --- /dev/null +++ b/docs/data-sources/application_api.md @@ -0,0 +1,42 @@ +--- +page_title: "zitadel_application_api Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing an API application belonging to a project, with all configuration possibilities. +--- + +# zitadel_application_api (Data Source) + +Datasource representing an API application belonging to a project, with all configuration possibilities. + +## Example Usage + +```terraform +data "zitadel_application_api" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + app_id = "123456789012345678" +} + +output "application_api" { + value = data.zitadel_application_api.default +} +``` + + +## Schema + +### Required + +- `app_id` (String) The ID of this resource. +- `project_id` (String) ID of the project + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `auth_method_type` (String) Auth method type +- `id` (String) The ID of this resource. +- `name` (String) Name of the application \ No newline at end of file diff --git a/docs/data-sources/application_oidc.md b/docs/data-sources/application_oidc.md new file mode 100644 index 00000000..0053d9a1 --- /dev/null +++ b/docs/data-sources/application_oidc.md @@ -0,0 +1,55 @@ +--- +page_title: "zitadel_application_oidc Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing an OIDC application belonging to a project, with all configuration possibilities. +--- + +# zitadel_application_oidc (Data Source) + +Datasource representing an OIDC application belonging to a project, with all configuration possibilities. + +## Example Usage + +```terraform +data "zitadel_application_oidc" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + app_id = "123456789012345678" +} + +output "application_oidc" { + value = data.zitadel_application_oidc.default +} +``` + + +## Schema + +### Required + +- `app_id` (String) The ID of this resource. +- `project_id` (String) ID of the project + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `access_token_role_assertion` (Boolean) Access token role assertion +- `access_token_type` (String) Access token type +- `additional_origins` (List of String) Additional origins +- `app_type` (String) App type +- `auth_method_type` (String) Auth method type +- `clock_skew` (String) Clockskew +- `dev_mode` (Boolean) Dev mode +- `grant_types` (List of String) Grant types +- `id` (String) The ID of this resource. +- `id_token_role_assertion` (Boolean) ID token role assertion +- `id_token_userinfo_assertion` (Boolean) Token userinfo assertion +- `name` (String) Name of the application +- `post_logout_redirect_uris` (List of String) Post logout redirect URIs +- `redirect_uris` (List of String) RedirectURIs +- `response_types` (List of String) Response type +- `version` (String) Version \ No newline at end of file diff --git a/docs/data-sources/default_oidc_settings.md b/docs/data-sources/default_oidc_settings.md new file mode 100644 index 00000000..0189bf60 --- /dev/null +++ b/docs/data-sources/default_oidc_settings.md @@ -0,0 +1,31 @@ +--- +page_title: "zitadel_default_oidc_settings Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing the default oidc settings. +--- + +# zitadel_default_oidc_settings (Data Source) + +Datasource representing the default oidc settings. + +## Example Usage + +```terraform +data "zitadel_default_oidc_settings" "default" {} + +output "oidc_settings" { + value = data.zitadel_default_oidc_settings.default +} +``` + + +## Schema + +### Read-Only + +- `access_token_lifetime` (String) lifetime duration of access tokens +- `id` (String) The ID of this resource. +- `id_token_lifetime` (String) lifetime duration of id tokens +- `refresh_token_expiration` (String) expiration duration of refresh tokens +- `refresh_token_idle_expiration` (String) expiration duration of idle refresh tokens diff --git a/docs/data-sources/human_user.md b/docs/data-sources/human_user.md new file mode 100644 index 00000000..386f0864 --- /dev/null +++ b/docs/data-sources/human_user.md @@ -0,0 +1,49 @@ +--- +page_title: "zitadel_human_user Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a human user situated under an organization, which then can be authorized through memberships or direct grants on other resources. +--- + +# zitadel_human_user (Data Source) + +Datasource representing a human user situated under an organization, which then can be authorized through memberships or direct grants on other resources. + +## Example Usage + +```terraform +data "zitadel_human_user" "default" { + org_id = data.zitadel_org.default.id + user_id = "123456789012345678" +} + +output "human_user" { + value = data.zitadel_human_user.default +} +``` + + +## Schema + +### Required + +- `org_id` (String) ID of the organization +- `user_id` (String) The ID of this resource. + +### Read-Only + +- `display_name` (String) Display name of the user +- `email` (String) Email of the user +- `first_name` (String) First name of the user +- `gender` (String) Gender of the user +- `id` (String) The ID of this resource. +- `is_email_verified` (Boolean) Is the email verified of the user, can only be true if password of the user is set +- `is_phone_verified` (Boolean) Is the phone verified of the user +- `last_name` (String) Last name of the user +- `login_names` (List of String) Loginnames +- `nick_name` (String) Nick name of the user +- `phone` (String) Phone of the user +- `preferred_language` (String) Preferred language of the user +- `preferred_login_name` (String) Preferred login name +- `state` (String) State of the user +- `user_name` (String) Username \ No newline at end of file diff --git a/docs/data-sources/idp_azure_ad.md b/docs/data-sources/idp_azure_ad.md new file mode 100644 index 00000000..fdc9be01 --- /dev/null +++ b/docs/data-sources/idp_azure_ad.md @@ -0,0 +1,39 @@ +--- +page_title: "zitadel_idp_azure_ad Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing an Azure AD IDP on the instance. +--- + +# zitadel_idp_azure_ad (Data Source) + +Datasource representing an Azure AD IDP on the instance. + +## Example Usage + +```terraform +data "zitadel_idp_azure_ad" "default" { + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Read-Only + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `email_verified` (Boolean) automatically mark emails as verified +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider +- `tenant_id` (String) the azure ad tenant id +- `tenant_type` (String) the azure ad tenant type diff --git a/docs/data-sources/idp_github.md b/docs/data-sources/idp_github.md new file mode 100644 index 00000000..567699cf --- /dev/null +++ b/docs/data-sources/idp_github.md @@ -0,0 +1,36 @@ +--- +page_title: "zitadel_idp_github Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a GitHub IDP on the instance. +--- + +# zitadel_idp_github (Data Source) + +Datasource representing a GitHub IDP on the instance. + +## Example Usage + +```terraform +data "zitadel_idp_github" "default" { + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Read-Only + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider diff --git a/docs/data-sources/idp_github_es.md b/docs/data-sources/idp_github_es.md new file mode 100644 index 00000000..9eb79692 --- /dev/null +++ b/docs/data-sources/idp_github_es.md @@ -0,0 +1,39 @@ +--- +page_title: "zitadel_idp_github_es Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a GitHub Enterprise IDP on the instance. +--- + +# zitadel_idp_github_es (Data Source) + +Datasource representing a GitHub Enterprise IDP on the instance. + +## Example Usage + +```terraform +data "zitadel_idp_github_es" "default" { + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Read-Only + +- `authorization_endpoint` (String) the providers authorization endpoint +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider +- `token_endpoint` (String) the providers token endpoint +- `user_endpoint` (String) the providers user endpoint diff --git a/docs/data-sources/idp_gitlab.md b/docs/data-sources/idp_gitlab.md new file mode 100644 index 00000000..4b2cb92f --- /dev/null +++ b/docs/data-sources/idp_gitlab.md @@ -0,0 +1,36 @@ +--- +page_title: "zitadel_idp_gitlab Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a GitLab IDP on the instance. +--- + +# zitadel_idp_gitlab (Data Source) + +Datasource representing a GitLab IDP on the instance. + +## Example Usage + +```terraform +data "zitadel_idp_gitlab" "default" { + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Read-Only + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider diff --git a/docs/data-sources/idp_gitlab_self_hosted.md b/docs/data-sources/idp_gitlab_self_hosted.md new file mode 100644 index 00000000..62b94782 --- /dev/null +++ b/docs/data-sources/idp_gitlab_self_hosted.md @@ -0,0 +1,37 @@ +--- +page_title: "zitadel_idp_gitlab_self_hosted Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a GitLab Self Hosted IDP on the instance. +--- + +# zitadel_idp_gitlab_self_hosted (Data Source) + +Datasource representing a GitLab Self Hosted IDP on the instance. + +## Example Usage + +```terraform +data "zitadel_idp_gitlab_self_hosted" "default" { + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Read-Only + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `issuer` (String) the providers issuer +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider diff --git a/docs/data-sources/idp_google.md b/docs/data-sources/idp_google.md new file mode 100644 index 00000000..4d7c1676 --- /dev/null +++ b/docs/data-sources/idp_google.md @@ -0,0 +1,36 @@ +--- +page_title: "zitadel_idp_google Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a Google IDP on the instance. +--- + +# zitadel_idp_google (Data Source) + +Datasource representing a Google IDP on the instance. + +## Example Usage + +```terraform +data "zitadel_idp_google" "default" { + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Read-Only + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider diff --git a/docs/data-sources/idp_ldap.md b/docs/data-sources/idp_ldap.md new file mode 100644 index 00000000..34aace90 --- /dev/null +++ b/docs/data-sources/idp_ldap.md @@ -0,0 +1,55 @@ +--- +page_title: "zitadel_idp_ldap Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing an LDAP IDP on the instance. +--- + +# zitadel_idp_ldap (Data Source) + +Datasource representing an LDAP IDP on the instance. + +## Example Usage + +```terraform +data "zitadel_idp_ldap" "default" { + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Read-Only + +- `avatar_url_attribute` (String) User attribute for the avatar url +- `base_dn` (String) Base DN for LDAP connections +- `bind_dn` (String) Bind DN for LDAP connections +- `bind_password` (String, Sensitive) Bind password for LDAP connections +- `display_name_attribute` (String) User attribute for the display name +- `email_attribute` (String) User attribute for the email +- `email_verified_attribute` (String) User attribute for the email verified state +- `first_name_attribute` (String) User attribute for the first name +- `id_attribute` (String) User attribute for the id +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `last_name_attribute` (String) User attribute for the last name +- `name` (String) Name of the IDP +- `nick_name_attribute` (String) User attribute for the nick name +- `phone_attribute` (String) User attribute for the phone +- `phone_verified_attribute` (String) User attribute for the phone verified state +- `preferred_language_attribute` (String) User attribute for the preferred language +- `preferred_username_attribute` (String) User attribute for the preferred username +- `profile_attribute` (String) User attribute for the profile +- `servers` (List of String) Servers to try in order for establishing LDAP connections +- `start_tls` (Boolean) Wether to use StartTLS for LDAP connections +- `timeout` (String) Timeout for LDAP connections +- `user_base` (String) User base for LDAP connections +- `user_filters` (Set of String) User filters for LDAP connections +- `user_object_classes` (Set of String) User object classes for LDAP connections diff --git a/docs/data-sources/machine_user.md b/docs/data-sources/machine_user.md new file mode 100644 index 00000000..22439163 --- /dev/null +++ b/docs/data-sources/machine_user.md @@ -0,0 +1,45 @@ +--- +page_title: "zitadel_machine_user Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a serviceaccount situated under an organization, which then can be authorized through memberships or direct grants on other resources. +--- + +# zitadel_machine_user (Data Source) + +Datasource representing a serviceaccount situated under an organization, which then can be authorized through memberships or direct grants on other resources. + +## Example Usage + +```terraform +data "zitadel_machine_user" "default" { + org_id = data.zitadel_org.default.id + user_id = "123456789012345678" +} + +output "machine_user" { + value = data.zitadel_machine_user.default +} +``` + + +## Schema + +### Required + +- `user_id` (String) The ID of this resource. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `access_token_type` (String) Access token type +- `description` (String) Description of the user +- `id` (String) The ID of this resource. +- `login_names` (List of String) Loginnames +- `name` (String) Name of the machine user +- `preferred_login_name` (String) Preferred login name +- `state` (String) State of the user +- `user_name` (String) Username \ No newline at end of file diff --git a/docs/data-sources/org.md b/docs/data-sources/org.md new file mode 100644 index 00000000..10527a93 --- /dev/null +++ b/docs/data-sources/org.md @@ -0,0 +1,35 @@ +--- +page_title: "zitadel_org Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing an organization in ZITADEL, which is the highest level after the instance and contains several other resource including policies if the configuration differs to the default policies on the instance. +--- + +# zitadel_org (Data Source) + +Datasource representing an organization in ZITADEL, which is the highest level after the instance and contains several other resource including policies if the configuration differs to the default policies on the instance. + +## Example Usage + +```terraform +data "zitadel_org" "default" { + id = "123456789012345678" +} + +output "org" { + value = data.zitadel_org.default +} +``` + + +## Schema + +### Required + +- `id` (String) ID of the organization + +### Read-Only + +- `name` (String) Name of the org. +- `primary_domain` (String) Primary domain of the org +- `state` (String) State of the org, supported values: ORG_STATE_UNSPECIFIED, ORG_STATE_ACTIVE, ORG_STATE_INACTIVE, ORG_STATE_REMOVED \ No newline at end of file diff --git a/docs/data-sources/org_idp_azure_ad.md b/docs/data-sources/org_idp_azure_ad.md new file mode 100644 index 00000000..407ac8d5 --- /dev/null +++ b/docs/data-sources/org_idp_azure_ad.md @@ -0,0 +1,44 @@ +--- +page_title: "zitadel_org_idp_azure_ad Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing an Azure AD IdP of the organization. +--- + +# zitadel_org_idp_azure_ad (Data Source) + +Datasource representing an Azure AD IdP of the organization. + +## Example Usage + +```terraform +data "zitadel_org_idp_azure_ad" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `email_verified` (Boolean) automatically mark emails as verified +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider +- `tenant_id` (String) the azure ad tenant id +- `tenant_type` (String) the azure ad tenant type diff --git a/docs/data-sources/org_idp_github.md b/docs/data-sources/org_idp_github.md new file mode 100644 index 00000000..b2348903 --- /dev/null +++ b/docs/data-sources/org_idp_github.md @@ -0,0 +1,41 @@ +--- +page_title: "zitadel_org_idp_github Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a GitHub IdP of the organization. +--- + +# zitadel_org_idp_github (Data Source) + +Datasource representing a GitHub IdP of the organization. + +## Example Usage + +```terraform +data "zitadel_org_idp_github" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider diff --git a/docs/data-sources/org_idp_github_es.md b/docs/data-sources/org_idp_github_es.md new file mode 100644 index 00000000..b2f1dfa1 --- /dev/null +++ b/docs/data-sources/org_idp_github_es.md @@ -0,0 +1,44 @@ +--- +page_title: "zitadel_org_idp_github_es Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a GitHub Enterprise IdP of the organization. +--- + +# zitadel_org_idp_github_es (Data Source) + +Datasource representing a GitHub Enterprise IdP of the organization. + +## Example Usage + +```terraform +data "zitadel_org_idp_github_es" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `authorization_endpoint` (String) the providers authorization endpoint +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider +- `token_endpoint` (String) the providers token endpoint +- `user_endpoint` (String) the providers user endpoint diff --git a/docs/data-sources/org_idp_gitlab.md b/docs/data-sources/org_idp_gitlab.md new file mode 100644 index 00000000..e2c0153b --- /dev/null +++ b/docs/data-sources/org_idp_gitlab.md @@ -0,0 +1,41 @@ +--- +page_title: "zitadel_org_idp_gitlab Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a GitLab IdP of the organization. +--- + +# zitadel_org_idp_gitlab (Data Source) + +Datasource representing a GitLab IdP of the organization. + +## Example Usage + +```terraform +data "zitadel_org_idp_gitlab" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider diff --git a/docs/data-sources/org_idp_gitlab_self_hosted.md b/docs/data-sources/org_idp_gitlab_self_hosted.md new file mode 100644 index 00000000..3853afeb --- /dev/null +++ b/docs/data-sources/org_idp_gitlab_self_hosted.md @@ -0,0 +1,42 @@ +--- +page_title: "zitadel_org_idp_gitlab_self_hosted Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a GitLab Self Hosted IdP of the organization. +--- + +# zitadel_org_idp_gitlab_self_hosted (Data Source) + +Datasource representing a GitLab Self Hosted IdP of the organization. + +## Example Usage + +```terraform +data "zitadel_org_idp_gitlab_self_hosted" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `issuer` (String) the providers issuer +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider diff --git a/docs/data-sources/org_idp_google.md b/docs/data-sources/org_idp_google.md new file mode 100644 index 00000000..24f31e35 --- /dev/null +++ b/docs/data-sources/org_idp_google.md @@ -0,0 +1,41 @@ +--- +page_title: "zitadel_org_idp_google Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a Google IdP of the organization. +--- + +# zitadel_org_idp_google (Data Source) + +Datasource representing a Google IdP of the organization. + +## Example Usage + +```terraform +data "zitadel_org_idp_google" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider diff --git a/docs/data-sources/org_idp_ldap.md b/docs/data-sources/org_idp_ldap.md new file mode 100644 index 00000000..b9c23273 --- /dev/null +++ b/docs/data-sources/org_idp_ldap.md @@ -0,0 +1,60 @@ +--- +page_title: "zitadel_org_idp_ldap Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing an LDAP IdP on the organization. +--- + +# zitadel_org_idp_ldap (Data Source) + +Datasource representing an LDAP IdP on the organization. + +## Example Usage + +```terraform +data "zitadel_org_idp_ldap" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `avatar_url_attribute` (String) User attribute for the avatar url +- `base_dn` (String) Base DN for LDAP connections +- `bind_dn` (String) Bind DN for LDAP connections +- `bind_password` (String, Sensitive) Bind password for LDAP connections +- `display_name_attribute` (String) User attribute for the display name +- `email_attribute` (String) User attribute for the email +- `email_verified_attribute` (String) User attribute for the email verified state +- `first_name_attribute` (String) User attribute for the first name +- `id_attribute` (String) User attribute for the id +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `last_name_attribute` (String) User attribute for the last name +- `name` (String) Name of the IDP +- `nick_name_attribute` (String) User attribute for the nick name +- `phone_attribute` (String) User attribute for the phone +- `phone_verified_attribute` (String) User attribute for the phone verified state +- `preferred_language_attribute` (String) User attribute for the preferred language +- `preferred_username_attribute` (String) User attribute for the preferred username +- `profile_attribute` (String) User attribute for the profile +- `servers` (List of String) Servers to try in order for establishing LDAP connections +- `start_tls` (Boolean) Wether to use StartTLS for LDAP connections +- `timeout` (String) Timeout for LDAP connections +- `user_base` (String) User base for LDAP connections +- `user_filters` (Set of String) User filters for LDAP connections +- `user_object_classes` (Set of String) User object classes for LDAP connections diff --git a/docs/data-sources/org_jwt_idp.md b/docs/data-sources/org_jwt_idp.md new file mode 100644 index 00000000..5dcd9395 --- /dev/null +++ b/docs/data-sources/org_jwt_idp.md @@ -0,0 +1,42 @@ +--- +page_title: "zitadel_org_jwt_idp Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a generic JWT IdP on the organization. +--- + +# zitadel_org_jwt_idp (Data Source) + +Datasource representing a generic JWT IdP on the organization. + +## Example Usage + +```terraform +data "zitadel_org_jwt_idp" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} + +output "org_idp_org_jwt_idp" { + value = data.zitadel_org_jwt_idp.default +} +``` + + +## Schema + +### Required + +- `idp_id` (String) The ID of this resource. +- `org_id` (String) ID of the organization + +### Read-Only + +- `auto_register` (Boolean) auto register for users from this idp +- `header_name` (String) the name of the header where the JWT is sent in, default is authorization +- `id` (String) The ID of this resource. +- `issuer` (String) the issuer of the jwt (for validation) +- `jwt_endpoint` (String) the endpoint where the jwt can be extracted +- `keys_endpoint` (String) the endpoint to the key (JWK) which are used to sign the JWT with +- `name` (String) Name of the IDP +- `styling_type` (String) Some identity providers specify the styling of the button to their login \ No newline at end of file diff --git a/docs/data-sources/org_oidc_idp.md b/docs/data-sources/org_oidc_idp.md new file mode 100644 index 00000000..1cfd6e33 --- /dev/null +++ b/docs/data-sources/org_oidc_idp.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_org_oidc_idp Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing a generic OIDC IdP on the organization. +--- + +# zitadel_org_oidc_idp (Data Source) + +Datasource representing a generic OIDC IdP on the organization. + +## Example Usage + +```terraform +data "zitadel_org_oidc_idp" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} + +output "org_oidc_idp" { + value = data.zitadel_org_oidc_idp.default +} +``` + + +## Schema + +### Required + +- `id` (String) The ID of this resource. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enabled if a new account in ZITADEL are created automatically on login with an external account +- `is_auto_update` (Boolean) enabled if a the ZITADEL account fields are updated automatically on each login +- `is_creation_allowed` (Boolean) enabled if users are able to create a new account in ZITADEL when using an external account +- `is_id_token_mapping` (Boolean) if true, provider information get mapped from the id token, not from the userinfo endpoint. +- `is_linking_allowed` (Boolean) enabled if users are able to link an existing ZITADEL user with an external account +- `issuer` (String) the issuer of the idp +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider \ No newline at end of file diff --git a/docs/data-sources/orgs.md b/docs/data-sources/orgs.md new file mode 100644 index 00000000..22bc0cac --- /dev/null +++ b/docs/data-sources/orgs.md @@ -0,0 +1,32 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "zitadel_orgs Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing an organization in ZITADEL, which is the highest level after the instance and contains several other resource including policies if the configuration differs to the default policies on the instance. +--- + +# zitadel_orgs (Data Source) + +Datasource representing an organization in ZITADEL, which is the highest level after the instance and contains several other resource including policies if the configuration differs to the default policies on the instance. + + + + +## Schema + +### Optional + +- `domain` (String) A domain of the org. +- `domain_method` (String) Method for querying orgs by domain, supported values: TEXT_QUERY_METHOD_EQUALS, TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE, TEXT_QUERY_METHOD_STARTS_WITH, TEXT_QUERY_METHOD_STARTS_WITH_IGNORE_CASE, TEXT_QUERY_METHOD_CONTAINS, TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE, TEXT_QUERY_METHOD_ENDS_WITH, TEXT_QUERY_METHOD_ENDS_WITH_IGNORE_CASE +- `name` (String) Name of the org. +- `name_method` (String) Method for querying orgs by name, supported values: TEXT_QUERY_METHOD_EQUALS, TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE, TEXT_QUERY_METHOD_STARTS_WITH, TEXT_QUERY_METHOD_STARTS_WITH_IGNORE_CASE, TEXT_QUERY_METHOD_CONTAINS, TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE, TEXT_QUERY_METHOD_ENDS_WITH, TEXT_QUERY_METHOD_ENDS_WITH_IGNORE_CASE +- `state` (String) State of the org, supported values: ORG_STATE_UNSPECIFIED, ORG_STATE_ACTIVE, ORG_STATE_INACTIVE, ORG_STATE_REMOVED + +### Read-Only + +- `id` (String) The ID of this resource. +- `ids` (List of String) A set of all organization IDs. +- `primary_domain` (String) Primary domain of the org + + diff --git a/docs/data-sources/project.md b/docs/data-sources/project.md new file mode 100644 index 00000000..83574494 --- /dev/null +++ b/docs/data-sources/project.md @@ -0,0 +1,44 @@ +--- +page_title: "zitadel_project Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing the project, which can then be granted to different organizations or users directly, containing different applications. +--- + +# zitadel_project (Data Source) + +Datasource representing the project, which can then be granted to different organizations or users directly, containing different applications. + +## Example Usage + +```terraform +data "zitadel_project" "default" { + org_id = data.zitadel_org.default.id + project_id = "123456789012345678" +} + +output "project" { + value = data.zitadel_project.default +} +``` + + +## Schema + +### Required + +- `project_id` (String) The ID of this resource. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `has_project_check` (Boolean) ZITADEL checks if the org of the user has permission to this project +- `id` (String) The ID of this resource. +- `name` (String) Name of the project +- `private_labeling_setting` (String) Defines from where the private labeling should be triggered +- `project_role_assertion` (Boolean) describes if roles of user should be added in token +- `project_role_check` (Boolean) ZITADEL checks if the user has at least one on this project +- `state` (String) State of the project \ No newline at end of file diff --git a/docs/data-sources/project_role.md b/docs/data-sources/project_role.md new file mode 100644 index 00000000..26a8e012 --- /dev/null +++ b/docs/data-sources/project_role.md @@ -0,0 +1,39 @@ +--- +page_title: "zitadel_project_role Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Datasource representing the project roles, which can be given as authorizations to users. +--- + +# zitadel_project_role (Data Source) + +Datasource representing the project roles, which can be given as authorizations to users. + +## Example Usage + +```terraform +data "zitadel_project_role" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + role_key = "key" +} + +output "project_role" { + value = data.zitadel_project_role.default +} +``` + + +## Schema + +### Required + +- `org_id` (String) ID of the organization +- `project_id` (String) ID of the project +- `role_key` (String) Key used for project role + +### Read-Only + +- `display_name` (String) Name used for project role +- `group` (String) Group used for project role +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/data-sources/trigger_actions.md b/docs/data-sources/trigger_actions.md new file mode 100644 index 00000000..195c77da --- /dev/null +++ b/docs/data-sources/trigger_actions.md @@ -0,0 +1,41 @@ +--- +page_title: "zitadel_trigger_actions Data Source - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing triggers, when actions get started +--- + +# zitadel_trigger_actions (Data Source) + +Resource representing triggers, when actions get started + +## Example Usage + +```terraform +data "zitadel_trigger_actions" "default" { + org_id = data.zitadel_org.default.id + flow_type = "FLOW_TYPE_EXTERNAL_AUTHENTICATION" + trigger_type = "TRIGGER_TYPE_POST_AUTHENTICATION" +} + +output "trigger_actions" { + value = data.zitadel_trigger_actions.default +} +``` + + +## Schema + +### Required + +- `flow_type` (String) Type of the flow to which the action triggers belong +- `trigger_type` (String) Trigger type on when the actions get triggered + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `action_ids` (Set of String) IDs of the triggered actions +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..5a360a7e --- /dev/null +++ b/docs/index.md @@ -0,0 +1,61 @@ +--- +page_title: "Provider: ZITADEL" +description: |- + Provider to manage resources on ZITADEL + +--- + +# ZITADEL Provider + +This provider allows the management of different resources on a ZITADEL instance. + +Through this provider it is possible to interact with the [ZITADEL API](https://docs.zitadel.com/docs/apis/introduction) to configure different aspects of the instance. + +Currently does not contain, but could be included in the future if demand exists: + +- instance as resource +- policies and settings on instance level as resource +- custom text resources + +Not planned to include as resources, as more administration task as a resource: + +- Import and export of organizations +- (re-)setting of password for human user +- initializing of OTP setup for human user +- initializing of passwordless setup for human user + +## Configuring the provider + +```terraform +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "1.0.0-alpha.16" + } + } +} + +provider "zitadel" { + domain = "localhost" + insecure = "true" + port = "8080" + project = "170832731415117995" + jwt_profile_file = "local-token" +} +``` + + +## Schema + +### Required + +- `domain` (String) Domain used to connect to the ZITADEL instance + +### Optional + +- `insecure` (Boolean) Use insecure connection +- `jwt_profile_file` (String) Path to the file containing credentials to connect to ZITADEL. Either 'jwt_profile_file' or 'jwt_profile_json' is required +- `jwt_profile_json` (String) JSON value of credentials to connect to ZITADEL. Either 'jwt_profile_file' or 'jwt_profile_json' is required +- `port` (String) Used port if not the default ports 80 or 443 are configured +- `token` (String) Path to the file containing credentials to connect to ZITADEL diff --git a/docs/resources/action.md b/docs/resources/action.md new file mode 100644 index 00000000..493b08d0 --- /dev/null +++ b/docs/resources/action.md @@ -0,0 +1,48 @@ +--- +page_title: "zitadel_action Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing an action belonging to an organization. +--- + +# zitadel_action (Resource) + +Resource representing an action belonging to an organization. + +## Example Usage + +```terraform +resource "zitadel_action" "default" { + org_id = data.zitadel_org.default.id + name = "actionname" + script = "testscript" + timeout = "10s" + allowed_to_fail = true +} +``` + + +## Schema + +### Required + +- `allowed_to_fail` (Boolean) when true, the next action will be called even if this action fails +- `name` (String) +- `script` (String) +- `timeout` (String) after which time the action will be terminated if not finished + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. +- `state` (Number) the state of the action + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import action.imported '123456789012345678:123456789012345678' +``` diff --git a/docs/resources/application_api.md b/docs/resources/application_api.md new file mode 100644 index 00000000..feb7c3fb --- /dev/null +++ b/docs/resources/application_api.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_application_api Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing an API application belonging to a project, with all configuration possibilities. +--- + +# zitadel_application_api (Resource) + +Resource representing an API application belonging to a project, with all configuration possibilities. + +## Example Usage + +```terraform +resource "zitadel_application_api" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + name = "applicationapi" + auth_method_type = "API_AUTH_METHOD_TYPE_BASIC" +} +``` + + +## Schema + +### Required + +- `name` (String) Name of the application +- `project_id` (String) ID of the project + +### Optional + +- `auth_method_type` (String) Auth method type, supported values: API_AUTH_METHOD_TYPE_BASIC, API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT +- `org_id` (String) ID of the organization + +### Read-Only + +- `client_id` (String, Sensitive) generated ID for this config +- `client_secret` (String, Sensitive) generated secret for this config +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import application_api.imported '123456789012345678:123456789012345678:123456789012345678:123456789012345678@zitadel:JuaDFFeOak5DGE655KCYPSAclSkbMVEJXXuX1lEMBT14eLMSs0A0qhafKX5SA2Df' +``` diff --git a/docs/resources/application_key.md b/docs/resources/application_key.md new file mode 100644 index 00000000..eb6ad06d --- /dev/null +++ b/docs/resources/application_key.md @@ -0,0 +1,49 @@ +--- +page_title: "zitadel_application_key Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a app key +--- + +# zitadel_application_key (Resource) + +Resource representing a app key + +## Example Usage + +```terraform +resource "zitadel_application_key" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + app_id = data.zitadel_application_api.default.id + key_type = "KEY_TYPE_JSON" + expiration_date = "2519-04-01T08:45:00Z" +} +``` + + +## Schema + +### Required + +- `app_id` (String) ID of the application +- `expiration_date` (String) Expiration date of the app key in the RFC3339 format +- `key_type` (String) Type of the app key, supported values: KEY_TYPE_UNSPECIFIED, KEY_TYPE_JSON +- `project_id` (String) ID of the project + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. +- `key_details` (String, Sensitive) Value of the app key + +## Import + +```terraform +# The resource can be imported using the ID format ``. +# You can use __SEMICOLON__ to escape :, e.g. +terraform import application_key.imported "123456789012345678:123456789012345678:123456789012345678:123456789012345678:$(cat ~/Downloads/123456789012345678.json | sed -e 's/:/__SEMICOLON__/g')" +``` diff --git a/docs/resources/application_oidc.md b/docs/resources/application_oidc.md new file mode 100644 index 00000000..e3c560dd --- /dev/null +++ b/docs/resources/application_oidc.md @@ -0,0 +1,74 @@ +--- +page_title: "zitadel_application_oidc Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing an OIDC application belonging to a project, with all configuration possibilities. +--- + +# zitadel_application_oidc (Resource) + +Resource representing an OIDC application belonging to a project, with all configuration possibilities. + +## Example Usage + +```terraform +resource "zitadel_application_oidc" "default" { + project_id = data.zitadel_project.default.id + org_id = data.zitadel_org.default.id + + name = "applicationoidc" + redirect_uris = ["https://localhost.com"] + response_types = ["OIDC_RESPONSE_TYPE_CODE"] + grant_types = ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE"] + post_logout_redirect_uris = ["https://localhost.com"] + app_type = "OIDC_APP_TYPE_WEB" + auth_method_type = "OIDC_AUTH_METHOD_TYPE_BASIC" + version = "OIDC_VERSION_1_0" + clock_skew = "0s" + dev_mode = true + access_token_type = "OIDC_TOKEN_TYPE_BEARER" + access_token_role_assertion = false + id_token_role_assertion = false + id_token_userinfo_assertion = false + additional_origins = [] +} +``` + + +## Schema + +### Required + +- `grant_types` (List of String) Grant types, supported values: OIDC_GRANT_TYPE_AUTHORIZATION_CODE, OIDC_GRANT_TYPE_IMPLICIT, OIDC_GRANT_TYPE_REFRESH_TOKEN, OIDC_GRANT_TYPE_DEVICE_CODE +- `name` (String) Name of the application +- `project_id` (String) ID of the project +- `redirect_uris` (List of String) RedirectURIs +- `response_types` (List of String) Response type, supported values: OIDC_RESPONSE_TYPE_CODE, OIDC_RESPONSE_TYPE_ID_TOKEN, OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN + +### Optional + +- `access_token_role_assertion` (Boolean) Access token role assertion +- `access_token_type` (String) Access token type, supported values: OIDC_TOKEN_TYPE_BEARER, OIDC_TOKEN_TYPE_JWT +- `additional_origins` (List of String) Additional origins +- `app_type` (String) App type, supported values: OIDC_APP_TYPE_WEB, OIDC_APP_TYPE_USER_AGENT, OIDC_APP_TYPE_NATIVE +- `auth_method_type` (String) Auth method type, supported values: OIDC_AUTH_METHOD_TYPE_BASIC, OIDC_AUTH_METHOD_TYPE_POST, OIDC_AUTH_METHOD_TYPE_NONE, OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT +- `clock_skew` (String) Clockskew +- `dev_mode` (Boolean) Dev mode +- `id_token_role_assertion` (Boolean) ID token role assertion +- `id_token_userinfo_assertion` (Boolean) Token userinfo assertion +- `org_id` (String) ID of the organization +- `post_logout_redirect_uris` (List of String) Post logout redirect URIs +- `version` (String) Version, supported values: OIDC_VERSION_1_0 + +### Read-Only + +- `client_id` (String, Sensitive) generated ID for this config +- `client_secret` (String, Sensitive) generated secret for this config +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import application_oidc.imported '123456789012345678:123456789012345678:123456789012345678:123456789012345678@zitadel:JuaDFFeOak5DGE655KCYPSAclSkbMVEJXXuX1lEMBT14eLMSs0A0qhafKX5SA2Df' +``` diff --git a/docs/resources/default_domain_claimed_message_text.md b/docs/resources/default_domain_claimed_message_text.md new file mode 100644 index 00000000..c66437ca --- /dev/null +++ b/docs/resources/default_domain_claimed_message_text.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_default_domain_claimed_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_default_domain_claimed_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_default_domain_claimed_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/default_domain_policy.md b/docs/resources/default_domain_policy.md new file mode 100644 index 00000000..db43c4a3 --- /dev/null +++ b/docs/resources/default_domain_policy.md @@ -0,0 +1,41 @@ +--- +page_title: "zitadel_default_domain_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the default domain policy. +--- + +# zitadel_default_domain_policy (Resource) + +Resource representing the default domain policy. + +## Example Usage + +```terraform +resource "zitadel_default_domain_policy" "default" { + user_login_must_be_domain = false + validate_org_domains = true + smtp_sender_address_matches_instance_domain = true +} +``` + + +## Schema + +### Required + +- `smtp_sender_address_matches_instance_domain` (Boolean) +- `user_login_must_be_domain` (Boolean) User login must be domain +- `validate_org_domains` (Boolean) Validate organization domains + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + + +```terraform +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_domain_policy.imported '' +``` diff --git a/docs/resources/default_init_message_text.md b/docs/resources/default_init_message_text.md new file mode 100644 index 00000000..679f16ee --- /dev/null +++ b/docs/resources/default_init_message_text.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_default_init_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_default_init_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_default_init_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/default_label_policy.md b/docs/resources/default_label_policy.md new file mode 100644 index 00000000..f4278797 --- /dev/null +++ b/docs/resources/default_label_policy.md @@ -0,0 +1,85 @@ +--- +page_title: "zitadel_default_label_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the default label policy. +--- + +# zitadel_default_label_policy (Resource) + +Resource representing the default label policy. + +## Example Usage + +```terraform +resource "zitadel_default_label_policy" "default" { + primary_color = "#5469d4" + hide_login_name_suffix = true + warn_color = "#cd3d56" + background_color = "#fafafa" + font_color = "#000000" + primary_color_dark = "#a5b4fc" + background_color_dark = "#111827" + warn_color_dark = "#ff3b5b" + font_color_dark = "#ffffff" + disable_watermark = false + set_active = true + logo_hash = filemd5("/path/to/logo.jpg") + logo_path = "/path/to/logo.jpg" + logo_dark_hash = filemd5("/path/to/logo_dark.jpg") + logo_dark_path = "/path/to/logo_dark.jpg" + icon_hash = filemd5("/path/to/icon.jpg") + icon_path = "/path/to/icon.jpg" + icon_dark_hash = filemd5("/path/to/icon_dark.jpg") + icon_dark_path = "/path/to/icon_dark.jpg" + font_hash = filemd5("/path/to/font.tff") + font_path = "/path/to/font.tff" +} +``` + + +## Schema + +### Required + +- `background_color` (String) hex value for background color +- `background_color_dark` (String) hex value for background color dark theme +- `disable_watermark` (Boolean) disable watermark +- `font_color` (String) hex value for font color +- `font_color_dark` (String) hex value for font color dark theme +- `hide_login_name_suffix` (Boolean) hides the org suffix on the login form if the scope "urn:zitadel:iam:org:domain:primary:{domainname}" is set. Details about this scope in https://docs.zitadel.ch/concepts#Reserved_Scopes +- `primary_color` (String) hex value for primary color +- `primary_color_dark` (String) hex value for primary color dark theme +- `warn_color` (String) hex value for warn color +- `warn_color_dark` (String) hex value for warn color dark theme + +### Optional + +- `font_hash` (String) +- `font_path` (String) +- `icon_dark_hash` (String) +- `icon_dark_path` (String) +- `icon_hash` (String) +- `icon_path` (String) +- `logo_dark_hash` (String) +- `logo_dark_path` (String) +- `logo_hash` (String) +- `logo_path` (String) +- `set_active` (Boolean) set the label policy active after creating/updating + +### Read-Only + +- `font_url` (String) +- `icon_url` (String) +- `icon_url_dark` (String) +- `id` (String) The ID of this resource. +- `logo_url` (String) +- `logo_url_dark` (String) + +## Import + + +```terraform +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_label_policy.imported '' +``` diff --git a/docs/resources/default_lockout_policy.md b/docs/resources/default_lockout_policy.md new file mode 100644 index 00000000..07cc2cc7 --- /dev/null +++ b/docs/resources/default_lockout_policy.md @@ -0,0 +1,37 @@ +--- +page_title: "zitadel_default_lockout_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the default lockout policy. +--- + +# zitadel_default_lockout_policy (Resource) + +Resource representing the default lockout policy. + +## Example Usage + +```terraform +resource "zitadel_default_lockout_policy" "default" { + max_password_attempts = "5" +} +``` + + +## Schema + +### Required + +- `max_password_attempts` (Number) Maximum password check attempts before the account gets locked. Attempts are reset as soon as the password is entered correctly or the password is reset. + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + + +```terraform +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_lockout_policy.imported '' +``` diff --git a/docs/resources/default_login_policy.md b/docs/resources/default_login_policy.md new file mode 100644 index 00000000..b678eb28 --- /dev/null +++ b/docs/resources/default_login_policy.md @@ -0,0 +1,78 @@ +--- +page_title: "zitadel_default_login_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the default login policy. +--- + +# zitadel_default_login_policy (Resource) + +Resource representing the default login policy. + +## Example Usage + +```terraform +resource "zitadel_default_login_policy" "default" { + user_login = true + allow_register = true + allow_external_idp = true + force_mfa = false + force_mfa_local_only = false + passwordless_type = "PASSWORDLESS_TYPE_ALLOWED" + hide_password_reset = "false" + password_check_lifetime = "240h0m0s" + external_login_check_lifetime = "240h0m0s" + multi_factor_check_lifetime = "24h0m0s" + mfa_init_skip_lifetime = "720h0m0s" + second_factor_check_lifetime = "24h0m0s" + ignore_unknown_usernames = true + default_redirect_uri = "localhost:8080" + second_factors = ["SECOND_FACTOR_TYPE_OTP", "SECOND_FACTOR_TYPE_U2F"] + multi_factors = ["MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION"] + idps = [data.zitadel_idp_google.default.id, data.zitadel_idp_azure_ad.default.id] + allow_domain_discovery = true + disable_login_with_email = true + disable_login_with_phone = true +} +``` + + +## Schema + +### Required + +- `allow_external_idp` (Boolean) defines if a user is allowed to add a defined identity provider. E.g. Google auth +- `allow_register` (Boolean) defines if a person is allowed to register a user on this organisation +- `default_redirect_uri` (String) defines where the user will be redirected to if the login is started without app context (e.g. from mail) +- `external_login_check_lifetime` (String) +- `force_mfa` (Boolean) defines if a user MUST use a multi factor to log in +- `force_mfa_local_only` (Boolean) if activated, ZITADEL only enforces MFA on local authentications. On authentications through MFA, ZITADEL won't prompt for MFA. +- `hide_password_reset` (Boolean) defines if password reset link should be shown in the login screen +- `ignore_unknown_usernames` (Boolean) defines if unknown username on login screen directly return an error or always display the password screen +- `mfa_init_skip_lifetime` (String) +- `multi_factor_check_lifetime` (String) +- `password_check_lifetime` (String) +- `passwordless_type` (String) defines if passwordless is allowed for users +- `second_factor_check_lifetime` (String) +- `user_login` (Boolean) defines if a user is allowed to login with his username and password + +### Optional + +- `allow_domain_discovery` (Boolean) if set to true, the suffix (@domain.com) of an unknown username input on the login screen will be matched against the org domains and will redirect to the registration of that organisation on success. +- `disable_login_with_email` (Boolean) defines if user can additionally (to the loginname) be identified by their verified email address +- `disable_login_with_phone` (Boolean) defines if user can additionally (to the loginname) be identified by their verified phone number +- `idps` (Set of String) allowed idps to login or register +- `multi_factors` (Set of String) allowed multi factors +- `second_factors` (Set of String) allowed second factors + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + + +```terraform +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_login_policy.imported '' +``` diff --git a/docs/resources/default_login_texts.md b/docs/resources/default_login_texts.md new file mode 100644 index 00000000..d1f9bd7c --- /dev/null +++ b/docs/resources/default_login_texts.md @@ -0,0 +1,809 @@ +--- +page_title: "zitadel_default_login_texts Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_default_login_texts (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_default_login_texts" "default" { + language = "en" + + email_verification_done_text = { + cancel_button_text = "example" + description = "example" + login_button_text = "example" + next_button_text = "example" + title = "example" + } + email_verification_text = { + code_label = "example" + description = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + external_registration_user_overview_text = { + back_button_text = "example" + description = "example" + email_label = "example" + firstname_label = "example" + language_label = "example" + lastname_label = "example" + next_button_text = "example" + nickname_label = "example" + phone_label = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + external_user_not_found_text = { + auto_register_button_text = "example" + description = "example" + link_button_text = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + } + footer_text = { + help = "example" + privacy_policy = "example" + tos = "example" + } + init_mfa_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + init_mfa_otp_text = { + cancel_button_text = "example" + code_label = "example" + description = "example" + description_otp = "example" + next_button_text = "example" + secret_label = "example" + title = "example" + } + init_mfa_prompt_text = { + description = "example" + next_button_text = "example" + otp_option = "example" + skip_button_text = "example" + title = "example" + u2f_option = "example" + } + init_mfa_u2f_text = { + description = "example" + error_retry = "example" + not_supported = "example" + register_token_button_text = "example" + title = "example" + token_name_label = "example" + } + init_password_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + init_password_text = { + code_label = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + initialize_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + initialize_user_text = { + code_label = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + linking_user_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + login_text = { + description = "example" + description_linking_process = "example" + external_user_description = "example" + login_name_label = "example" + login_name_placeholder = "example" + next_button_text = "example" + register_button_text = "example" + title = "example" + title_linking_process = "example" + user_must_be_member_of_org = "example" + user_name_placeholder = "example" + } + logout_text = { + description = "example" + login_button_text = "example" + title = "example" + } + mfa_providers_text = { + choose_other = "example" + otp = "example" + u2f = "example" + } + password_change_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + password_change_text = { + cancel_button_text = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + old_password_label = "example" + title = "example" + } + password_reset_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + password_text = { + back_button_text = "example" + confirmation = "example" + description = "example" + has_lowercase = "example" + has_number = "example" + has_symbol = "example" + has_uppercase = "example" + min_length = "example" + next_button_text = "example" + password_label = "example" + reset_link_text = "example" + title = "example" + } + passwordless_prompt_text = { + description = "example" + description_init = "example" + next_button_text = "example" + passwordless_button_text = "example" + skip_button_text = "example" + title = "example" + } + passwordless_registration_done_text = { + cancel_button_text = "example" + description = "example" + description_close = "example" + next_button_text = "example" + title = "example" + } + passwordless_registration_text = { + description = "example" + error_retry = "example" + not_supported = "example" + register_token_button_text = "example" + title = "example" + token_name_label = "example" + } + passwordless_text = { + description = "example" + error_retry = "example" + login_with_pw_button_text = "example" + not_supported = "example" + title = "example" + validate_token_button_text = "example" + } + registration_option_text = { + description = "example" + external_login_description = "example" + title = "example" + user_name_button_text = "example" + } + registration_org_text = { + description = "example" + email_label = "example" + firstname_label = "example" + lastname_label = "example" + orgname_label = "example" + password_confirm_label = "example" + password_label = "example" + privacy_link_text = "example" + save_button_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + registration_user_text = { + back_button_text = "example" + description = "example" + description_org_register = "example" + email_label = "example" + firstname_label = "example" + gender_label = "example" + language_label = "example" + lastname_label = "example" + next_button_text = "example" + password_confirm_label = "example" + password_label = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + select_account_text = { + description = "example" + description_linking_process = "example" + other_user = "example" + session_state_active = "example" + session_state_inactive = "example" + title = "example" + title_linking_process = "example" + user_must_be_member_of_org = "example" + } + success_login_text = { + auto_redirect_description = "example" + next_button_text = "example" + redirected_description = "example" + title = "example" + } + username_change_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + username_change_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + username_label = "example" + } + verify_mfa_otp_text = { + code_label = "example" + description = "example" + next_button_text = "example" + title = "example" + } + verify_mfa_u2f_text = { + description = "example" + error_retry = "example" + not_supported = "example" + title = "example" + validate_token_text = "example" + } +} +``` + + +## Schema + +### Required + +- `language` (String) + +### Optional + +- `email_verification_done_text` (Attributes) (see [below for nested schema](#nestedatt--email_verification_done_text)) +- `email_verification_text` (Attributes) (see [below for nested schema](#nestedatt--email_verification_text)) +- `external_registration_user_overview_text` (Attributes) (see [below for nested schema](#nestedatt--external_registration_user_overview_text)) +- `external_user_not_found_text` (Attributes) (see [below for nested schema](#nestedatt--external_user_not_found_text)) +- `footer_text` (Attributes) (see [below for nested schema](#nestedatt--footer_text)) +- `init_mfa_done_text` (Attributes) (see [below for nested schema](#nestedatt--init_mfa_done_text)) +- `init_mfa_otp_text` (Attributes) (see [below for nested schema](#nestedatt--init_mfa_otp_text)) +- `init_mfa_prompt_text` (Attributes) (see [below for nested schema](#nestedatt--init_mfa_prompt_text)) +- `init_mfa_u2f_text` (Attributes) (see [below for nested schema](#nestedatt--init_mfa_u2f_text)) +- `init_password_done_text` (Attributes) (see [below for nested schema](#nestedatt--init_password_done_text)) +- `init_password_text` (Attributes) (see [below for nested schema](#nestedatt--init_password_text)) +- `initialize_done_text` (Attributes) (see [below for nested schema](#nestedatt--initialize_done_text)) +- `initialize_user_text` (Attributes) (see [below for nested schema](#nestedatt--initialize_user_text)) +- `linking_user_done_text` (Attributes) (see [below for nested schema](#nestedatt--linking_user_done_text)) +- `login_text` (Attributes) (see [below for nested schema](#nestedatt--login_text)) +- `logout_text` (Attributes) (see [below for nested schema](#nestedatt--logout_text)) +- `mfa_providers_text` (Attributes) (see [below for nested schema](#nestedatt--mfa_providers_text)) +- `password_change_done_text` (Attributes) (see [below for nested schema](#nestedatt--password_change_done_text)) +- `password_change_text` (Attributes) (see [below for nested schema](#nestedatt--password_change_text)) +- `password_reset_done_text` (Attributes) (see [below for nested schema](#nestedatt--password_reset_done_text)) +- `password_text` (Attributes) (see [below for nested schema](#nestedatt--password_text)) +- `passwordless_prompt_text` (Attributes) (see [below for nested schema](#nestedatt--passwordless_prompt_text)) +- `passwordless_registration_done_text` (Attributes) (see [below for nested schema](#nestedatt--passwordless_registration_done_text)) +- `passwordless_registration_text` (Attributes) (see [below for nested schema](#nestedatt--passwordless_registration_text)) +- `passwordless_text` (Attributes) (see [below for nested schema](#nestedatt--passwordless_text)) +- `registration_option_text` (Attributes) (see [below for nested schema](#nestedatt--registration_option_text)) +- `registration_org_text` (Attributes) (see [below for nested schema](#nestedatt--registration_org_text)) +- `registration_user_text` (Attributes) (see [below for nested schema](#nestedatt--registration_user_text)) +- `select_account_text` (Attributes) (see [below for nested schema](#nestedatt--select_account_text)) +- `success_login_text` (Attributes) (see [below for nested schema](#nestedatt--success_login_text)) +- `username_change_done_text` (Attributes) (see [below for nested schema](#nestedatt--username_change_done_text)) +- `username_change_text` (Attributes) (see [below for nested schema](#nestedatt--username_change_text)) +- `verify_mfa_otp_text` (Attributes) (see [below for nested schema](#nestedatt--verify_mfa_otp_text)) +- `verify_mfa_u2f_text` (Attributes) (see [below for nested schema](#nestedatt--verify_mfa_u2f_text)) + +### Read-Only + +- `id` (String) The ID of this resource. + + +### Nested Schema for `email_verification_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `login_button_text` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `email_verification_text` + +Optional: + +- `code_label` (String) +- `description` (String) +- `next_button_text` (String) +- `resend_button_text` (String) +- `title` (String) + + + +### Nested Schema for `external_registration_user_overview_text` + +Optional: + +- `back_button_text` (String) +- `description` (String) +- `email_label` (String) +- `firstname_label` (String) +- `language_label` (String) +- `lastname_label` (String) +- `next_button_text` (String) +- `nickname_label` (String) +- `phone_label` (String) +- `privacy_confirm` (String) +- `privacy_link_text` (String) +- `title` (String) +- `tos_and_privacy_label` (String) +- `tos_confirm` (String) +- `tos_link_text` (String) +- `username_label` (String) + + + +### Nested Schema for `external_user_not_found_text` + +Optional: + +- `auto_register_button_text` (String) +- `description` (String) +- `link_button_text` (String) +- `privacy_confirm` (String) +- `privacy_link_text` (String) +- `title` (String) +- `tos_and_privacy_label` (String) +- `tos_confirm` (String) +- `tos_link_text` (String) + + + +### Nested Schema for `footer_text` + +Optional: + +- `help` (String) +- `privacy_policy` (String) +- `tos` (String) + + + +### Nested Schema for `init_mfa_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `init_mfa_otp_text` + +Optional: + +- `cancel_button_text` (String) +- `code_label` (String) +- `description` (String) +- `description_otp` (String) +- `next_button_text` (String) +- `secret_label` (String) +- `title` (String) + + + +### Nested Schema for `init_mfa_prompt_text` + +Optional: + +- `description` (String) +- `next_button_text` (String) +- `otp_option` (String) +- `skip_button_text` (String) +- `title` (String) +- `u2f_option` (String) + + + +### Nested Schema for `init_mfa_u2f_text` + +Optional: + +- `description` (String) +- `error_retry` (String) +- `not_supported` (String) +- `register_token_button_text` (String) +- `title` (String) +- `token_name_label` (String) + + + +### Nested Schema for `init_password_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `init_password_text` + +Optional: + +- `code_label` (String) +- `description` (String) +- `new_password_confirm_label` (String) +- `new_password_label` (String) +- `next_button_text` (String) +- `resend_button_text` (String) +- `title` (String) + + + +### Nested Schema for `initialize_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `initialize_user_text` + +Optional: + +- `code_label` (String) +- `description` (String) +- `new_password_confirm_label` (String) +- `new_password_label` (String) +- `next_button_text` (String) +- `resend_button_text` (String) +- `title` (String) + + + +### Nested Schema for `linking_user_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `login_text` + +Optional: + +- `description` (String) +- `description_linking_process` (String) +- `external_user_description` (String) +- `login_name_label` (String) +- `login_name_placeholder` (String) +- `next_button_text` (String) +- `register_button_text` (String) +- `title` (String) +- `title_linking_process` (String) +- `user_must_be_member_of_org` (String) +- `user_name_placeholder` (String) + + + +### Nested Schema for `logout_text` + +Optional: + +- `description` (String) +- `login_button_text` (String) +- `title` (String) + + + +### Nested Schema for `mfa_providers_text` + +Optional: + +- `choose_other` (String) +- `otp` (String) +- `u2f` (String) + + + +### Nested Schema for `password_change_done_text` + +Optional: + +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `password_change_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `new_password_confirm_label` (String) +- `new_password_label` (String) +- `next_button_text` (String) +- `old_password_label` (String) +- `title` (String) + + + +### Nested Schema for `password_reset_done_text` + +Optional: + +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `password_text` + +Optional: + +- `back_button_text` (String) +- `confirmation` (String) +- `description` (String) +- `has_lowercase` (String) +- `has_number` (String) +- `has_symbol` (String) +- `has_uppercase` (String) +- `min_length` (String) +- `next_button_text` (String) +- `password_label` (String) +- `reset_link_text` (String) +- `title` (String) + + + +### Nested Schema for `passwordless_prompt_text` + +Optional: + +- `description` (String) +- `description_init` (String) +- `next_button_text` (String) +- `passwordless_button_text` (String) +- `skip_button_text` (String) +- `title` (String) + + + +### Nested Schema for `passwordless_registration_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `description_close` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `passwordless_registration_text` + +Optional: + +- `description` (String) +- `error_retry` (String) +- `not_supported` (String) +- `register_token_button_text` (String) +- `title` (String) +- `token_name_label` (String) + + + +### Nested Schema for `passwordless_text` + +Optional: + +- `description` (String) +- `error_retry` (String) +- `login_with_pw_button_text` (String) +- `not_supported` (String) +- `title` (String) +- `validate_token_button_text` (String) + + + +### Nested Schema for `registration_option_text` + +Optional: + +- `description` (String) +- `external_login_description` (String) +- `login_button_text` (String) +- `title` (String) +- `user_name_button_text` (String) + + + +### Nested Schema for `registration_org_text` + +Optional: + +- `description` (String) +- `email_label` (String) +- `firstname_label` (String) +- `lastname_label` (String) +- `orgname_label` (String) +- `password_confirm_label` (String) +- `password_label` (String) +- `privacy_confirm` (String) +- `privacy_link_text` (String) +- `save_button_text` (String) +- `title` (String) +- `tos_and_privacy_label` (String) +- `tos_confirm` (String) +- `tos_link_text` (String) +- `username_label` (String) + + + +### Nested Schema for `registration_user_text` + +Optional: + +- `back_button_text` (String) +- `description` (String) +- `description_org_register` (String) +- `email_label` (String) +- `firstname_label` (String) +- `gender_label` (String) +- `language_label` (String) +- `lastname_label` (String) +- `next_button_text` (String) +- `password_confirm_label` (String) +- `password_label` (String) +- `privacy_confirm` (String) +- `privacy_link_text` (String) +- `title` (String) +- `tos_and_privacy_label` (String) +- `tos_confirm` (String) +- `tos_link_text` (String) +- `username_label` (String) + + + +### Nested Schema for `select_account_text` + +Optional: + +- `description` (String) +- `description_linking_process` (String) +- `other_user` (String) +- `session_state_active` (String) +- `session_state_inactive` (String) +- `title` (String) +- `title_linking_process` (String) +- `user_must_be_member_of_org` (String) + + + +### Nested Schema for `success_login_text` + +Optional: + +- `auto_redirect_description` (String) Text to describe that auto-redirect should happen after successful login +- `next_button_text` (String) +- `redirected_description` (String) Text to describe that the window can be closed after redirect +- `title` (String) + + + +### Nested Schema for `username_change_done_text` + +Optional: + +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `username_change_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) +- `username_label` (String) + + + +### Nested Schema for `verify_mfa_otp_text` + +Optional: + +- `code_label` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `verify_mfa_u2f_text` + +Optional: + +- `description` (String) +- `error_retry` (String) +- `not_supported` (String) +- `title` (String) +- `validate_token_text` (String) diff --git a/docs/resources/default_notification_policy.md b/docs/resources/default_notification_policy.md new file mode 100644 index 00000000..945bfc0a --- /dev/null +++ b/docs/resources/default_notification_policy.md @@ -0,0 +1,37 @@ +--- +page_title: "zitadel_default_notification_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the default notification policy. +--- + +# zitadel_default_notification_policy (Resource) + +Resource representing the default notification policy. + +## Example Usage + +```terraform +resource "zitadel_default_notification_policy" "default" { + password_change = false +} +``` + + +## Schema + +### Required + +- `password_change` (Boolean) Send notification if a user changes his password + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + + +```terraform +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_notification_policy.imported '' +``` diff --git a/docs/resources/default_oidc_settings.md b/docs/resources/default_oidc_settings.md new file mode 100644 index 00000000..e89e27fa --- /dev/null +++ b/docs/resources/default_oidc_settings.md @@ -0,0 +1,35 @@ +--- +page_title: "zitadel_default_oidc_settings Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the default oidc settings. +--- + +# zitadel_default_oidc_settings (Resource) + +Resource representing the default oidc settings. + +## Example Usage + +```terraform +resource "zitadel_default_oidc_settings" "default" { + access_token_lifetime = "12h0m0s" + id_token_lifetime = "12h0m0s" + refresh_token_expiration = "720h0m0s" + refresh_token_idle_expiration = "2160h0m0s" +} +``` + + +## Schema + +### Required + +- `access_token_lifetime` (String) lifetime duration of access tokens +- `id_token_lifetime` (String) lifetime duration of id tokens +- `refresh_token_expiration` (String) expiration duration of refresh tokens +- `refresh_token_idle_expiration` (String) expiration duration of idle refresh tokens + +### Read-Only + +- `id` (String) The ID of this resource. diff --git a/docs/resources/default_password_change_message_text.md b/docs/resources/default_password_change_message_text.md new file mode 100644 index 00000000..f3b6270b --- /dev/null +++ b/docs/resources/default_password_change_message_text.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_default_password_change_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_default_password_change_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_default_password_change_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. diff --git a/docs/resources/default_password_complexity_policy.md b/docs/resources/default_password_complexity_policy.md new file mode 100644 index 00000000..4b426278 --- /dev/null +++ b/docs/resources/default_password_complexity_policy.md @@ -0,0 +1,45 @@ +--- +page_title: "zitadel_default_password_complexity_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the default password complexity policy. +--- + +# zitadel_default_password_complexity_policy (Resource) + +Resource representing the default password complexity policy. + +## Example Usage + +```terraform +resource "zitadel_default_password_complexity_policy" "default" { + min_length = "8" + has_uppercase = true + has_lowercase = true + has_number = true + has_symbol = true +} +``` + + +## Schema + +### Required + +- `has_lowercase` (Boolean) defines if the password MUST contain a lower case letter +- `has_number` (Boolean) defines if the password MUST contain a number +- `has_symbol` (Boolean) defines if the password MUST contain a symbol. E.g. "$" +- `has_uppercase` (Boolean) defines if the password MUST contain an upper case letter +- `min_length` (Number) Minimal length for the password + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + + +```terraform +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_password_complexity_policy.imported '' +``` diff --git a/docs/resources/default_password_reset_message_text.md b/docs/resources/default_password_reset_message_text.md new file mode 100644 index 00000000..f05746b8 --- /dev/null +++ b/docs/resources/default_password_reset_message_text.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_default_password_reset_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_default_password_reset_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_default_password_reset_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/default_passwordless_registration_message_text.md b/docs/resources/default_passwordless_registration_message_text.md new file mode 100644 index 00000000..e440b550 --- /dev/null +++ b/docs/resources/default_passwordless_registration_message_text.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_default_passwordless_registration_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_default_passwordless_registration_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_default_passwordless_registration_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/default_privacy_policy.md b/docs/resources/default_privacy_policy.md new file mode 100644 index 00000000..c5d034e5 --- /dev/null +++ b/docs/resources/default_privacy_policy.md @@ -0,0 +1,42 @@ +--- +page_title: "zitadel_default_privacy_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the default privacy policy. +--- + +# zitadel_default_privacy_policy (Resource) + +Resource representing the default privacy policy. + +## Example Usage + +```terraform +resource "zitadel_default_privacy_policy" "default" { + tos_link = "https://example.com/tos" + privacy_link = "https://example.com/privacy" + help_link = "https://example.com/help" + support_email = "support@example.com" +} +``` + + +## Schema + +### Required + +- `help_link` (String) +- `privacy_link` (String) +- `support_email` (String) +- `tos_link` (String) + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_privacy_policy.imported '' +``` diff --git a/docs/resources/default_verify_email_message_text.md b/docs/resources/default_verify_email_message_text.md new file mode 100644 index 00000000..bcb80321 --- /dev/null +++ b/docs/resources/default_verify_email_message_text.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_default_verify_email_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_default_verify_email_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_default_verify_email_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/default_verify_phone_message_text.md b/docs/resources/default_verify_phone_message_text.md new file mode 100644 index 00000000..b164d566 --- /dev/null +++ b/docs/resources/default_verify_phone_message_text.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_default_verify_phone_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_default_verify_phone_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_default_verify_phone_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/domain.md b/docs/resources/domain.md new file mode 100644 index 00000000..598b90a5 --- /dev/null +++ b/docs/resources/domain.md @@ -0,0 +1,45 @@ +--- +page_title: "zitadel_domain Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a domain of the organization. +--- + +# zitadel_domain (Resource) + +Resource representing a domain of the organization. + +## Example Usage + +```terraform +resource "zitadel_domain" "default" { + org_id = data.zitadel_org.default.id + name = "zitadel.default.127.0.0.1.sslip.io" + is_primary = false +} +``` + + +## Schema + +### Required + +- `name` (String) Name of the domain + +### Optional + +- `is_primary` (Boolean) Is domain primary +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. +- `is_verified` (Boolean) Is domain verified +- `validation_type` (Number) Validation type + +## Import + +```terraform +# The resource can be imported using the ID format `name[:org_id]`, e.g. +terraform import domain.imported 'example.com:123456789012345678' +``` diff --git a/docs/resources/domain_claimed_message_text.md b/docs/resources/domain_claimed_message_text.md new file mode 100644 index 00000000..9b1fe46e --- /dev/null +++ b/docs/resources/domain_claimed_message_text.md @@ -0,0 +1,49 @@ +--- +page_title: "zitadel_domain_claimed_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_domain_claimed_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_domain_claimed_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) +- `org_id` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/domain_policy.md b/docs/resources/domain_policy.md new file mode 100644 index 00000000..84259426 --- /dev/null +++ b/docs/resources/domain_policy.md @@ -0,0 +1,45 @@ +--- +page_title: "zitadel_domain_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the custom domain policy of an organization. +--- + +# zitadel_domain_policy (Resource) + +Resource representing the custom domain policy of an organization. + +## Example Usage + +```terraform +resource "zitadel_domain_policy" "default" { + org_id = data.zitadel_org.default.id + user_login_must_be_domain = false + validate_org_domains = true + smtp_sender_address_matches_instance_domain = true +} +``` + + +## Schema + +### Required + +- `smtp_sender_address_matches_instance_domain` (Boolean) +- `user_login_must_be_domain` (Boolean) User login must be domain +- `validate_org_domains` (Boolean) Validate organization domains + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import domain_policy.imported '123456789012345678' +``` diff --git a/docs/resources/human_user.md b/docs/resources/human_user.md new file mode 100644 index 00000000..e8f5740f --- /dev/null +++ b/docs/resources/human_user.md @@ -0,0 +1,68 @@ +--- +page_title: "zitadel_human_user Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a human user situated under an organization, which then can be authorized through memberships or direct grants on other resources. +--- + +# zitadel_human_user (Resource) + +**Caution: Email can only be set verified if a password is set for the user, either with initial_password or during runtime** + +Resource representing a human user situated under an organization, which then can be authorized through memberships or direct grants on other resources. + +## Example Usage + +```terraform +resource "zitadel_human_user" "default" { + org_id = data.zitadel_org.default.id + user_name = "humanfull@localhost.com" + first_name = "firstname" + last_name = "lastname" + nick_name = "nickname" + display_name = "displayname" + preferred_language = "de" + gender = "GENDER_MALE" + phone = "+41799999999" + is_phone_verified = true + email = "test@zitadel.com" + is_email_verified = true + initial_password = "Password1!" +} +``` + + +## Schema + +### Required + +- `email` (String) Email of the user +- `first_name` (String) First name of the user +- `last_name` (String) Last name of the user +- `user_name` (String) Username + +### Optional + +- `display_name` (String) Display name of the user +- `gender` (String) Gender of the user, supported values: GENDER_UNSPECIFIED, GENDER_FEMALE, GENDER_MALE, GENDER_DIVERSE +- `initial_password` (String, Sensitive) Initially set password for the user, not changeable after creation +- `is_email_verified` (Boolean) Is the email verified of the user, can only be true if password of the user is set +- `is_phone_verified` (Boolean) Is the phone verified of the user +- `nick_name` (String) Nick name of the user +- `org_id` (String) ID of the organization +- `phone` (String) Phone of the user +- `preferred_language` (String) Preferred language of the user + +### Read-Only + +- `id` (String) The ID of this resource. +- `login_names` (List of String) Loginnames +- `preferred_login_name` (String) Preferred login name +- `state` (String) State of the user + +## Import + +```terraform +# The resource can be imported using the ID format `id[:org_id][:initial_password]>`, e.g. +terraform import human_user.imported '123456789012345678:123456789012345678:Password1!' +``` diff --git a/docs/resources/idp_azure_ad.md b/docs/resources/idp_azure_ad.md new file mode 100644 index 00000000..fa321402 --- /dev/null +++ b/docs/resources/idp_azure_ad.md @@ -0,0 +1,58 @@ +--- +page_title: "zitadel_idp_azure_ad Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing an Azure AD IDP on the instance. +--- + +# zitadel_idp_azure_ad (Resource) + +Resource representing an Azure AD IDP on the instance. + +## Example Usage + +```terraform +resource "zitadel_idp_azure_ad" "default" { + name = "Azure AD" + client_id = "9065bfc8-a08a..." + client_secret = "H2n***" + scopes = ["openid", "profile", "email", "User.Read"] + tenant_type = "AZURE_AD_TENANT_TYPE_ORGANISATIONS" + email_verified = true + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `email_verified` (Boolean) automatically mark emails as verified +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account + +### Optional + +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider +- `tenant_id` (String) if tenant_id is not set, the tenant_type is used +- `tenant_type` (String) the azure ad tenant type + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import idp_azure_ad.imported '123456789012345678:12345678-1234-1234-1234-123456789012' +``` diff --git a/docs/resources/idp_github.md b/docs/resources/idp_github.md new file mode 100644 index 00000000..985ace58 --- /dev/null +++ b/docs/resources/idp_github.md @@ -0,0 +1,53 @@ +--- +page_title: "zitadel_idp_github Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a GitHub IDP on the instance. +--- + +# zitadel_idp_github (Resource) + +Resource representing a GitHub IDP on the instance. + +## Example Usage + +```terraform +resource "zitadel_idp_github" "default" { + name = "GitHub" + client_id = "86a165..." + client_secret = "*****afdbac18" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account + +### Optional + +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import idp_github.imported '123456789012345678:1234567890123456781234567890123456787890' +``` diff --git a/docs/resources/idp_github_es.md b/docs/resources/idp_github_es.md new file mode 100644 index 00000000..05395a96 --- /dev/null +++ b/docs/resources/idp_github_es.md @@ -0,0 +1,59 @@ +--- +page_title: "zitadel_idp_github_es Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a GitHub Enterprise IDP on the instance. +--- + +# zitadel_idp_github_es (Resource) + +Resource representing a GitHub Enterprise IDP on the instance. + +## Example Usage + +```terraform +resource "zitadel_idp_github_es" "default" { + name = "GitHub Enterprise Server" + client_id = "86a165..." + client_secret = "*****afdbac18" + scopes = ["openid", "profile", "email"] + authorization_endpoint = "https://auth.endpoint" + token_endpoint = "https://token.endpoint" + user_endpoint = "https://user.endpoint" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `authorization_endpoint` (String) the providers authorization endpoint +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account +- `token_endpoint` (String) the providers token endpoint +- `user_endpoint` (String) the providers user endpoint + +### Optional + +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import idp_github_es.imported '123456789012345678:1234567890123456781234567890123456787890' +``` diff --git a/docs/resources/idp_gitlab.md b/docs/resources/idp_gitlab.md new file mode 100644 index 00000000..6e7f26ed --- /dev/null +++ b/docs/resources/idp_gitlab.md @@ -0,0 +1,53 @@ +--- +page_title: "zitadel_idp_gitlab Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a GitLab IDP on the instance. +--- + +# zitadel_idp_gitlab (Resource) + +Resource representing a GitLab IDP on the instance. + +## Example Usage + +```terraform +resource "zitadel_idp_gitlab" "default" { + name = "GitLab" + client_id = "15765e..." + client_secret = "*****abcxyz" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account + +### Optional + +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import idp_gitlab.imported '123456789012345678:1234567890abcdef' +``` diff --git a/docs/resources/idp_gitlab_self_hosted.md b/docs/resources/idp_gitlab_self_hosted.md new file mode 100644 index 00000000..f982eff7 --- /dev/null +++ b/docs/resources/idp_gitlab_self_hosted.md @@ -0,0 +1,55 @@ +--- +page_title: "zitadel_idp_gitlab_self_hosted Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a GitLab Self Hosted IDP on the instance. +--- + +# zitadel_idp_gitlab_self_hosted (Resource) + +Resource representing a GitLab Self Hosted IDP on the instance. + +## Example Usage + +```terraform +resource "zitadel_idp_gitlab_self_hosted" "default" { + name = "GitLab Self Hosted" + client_id = "15765e..." + client_secret = "*****abcxyz" + scopes = ["openid", "profile", "email"] + issuer = "https://my.issuer" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account +- `issuer` (String) the providers issuer + +### Optional + +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import idp_gitlab_self_hosted.imported '123456789012345678:1234567890abcdef' +``` diff --git a/docs/resources/idp_google.md b/docs/resources/idp_google.md new file mode 100644 index 00000000..e8ff9b9e --- /dev/null +++ b/docs/resources/idp_google.md @@ -0,0 +1,53 @@ +--- +page_title: "zitadel_idp_google Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a Google IDP on the instance. +--- + +# zitadel_idp_google (Resource) + +Resource representing a Google IDP on the instance. + +## Example Usage + +```terraform +resource "zitadel_idp_google" "default" { + name = "Google" + client_id = "182902..." + client_secret = "GOCSPX-*****" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account + +### Optional + +- `name` (String) Name of the IDP +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import idp_google.imported '123456789012345678:G1234567890123' +``` diff --git a/docs/resources/idp_ldap.md b/docs/resources/idp_ldap.md new file mode 100644 index 00000000..31e76f5e --- /dev/null +++ b/docs/resources/idp_ldap.md @@ -0,0 +1,81 @@ +--- +page_title: "zitadel_idp_ldap Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing an LDAP IDP on the instance. +--- + +# zitadel_idp_ldap (Resource) + +Resource representing an LDAP IDP on the instance. + +## Example Usage + +```terraform +resource "zitadel_idp_ldap" "default" { + name = "LDAP" + servers = ["ldaps://my.primary.server:389", "ldaps://my.secondary.server:389"] + start_tls = false + base_dn = "dc=example,dc=com" + bind_dn = "cn=admin,dc=example,dc=com" + bind_password = "Password1!" + user_base = "dn" + user_object_classes = ["inetOrgPerson"] + user_filters = ["uid", "email"] + timeout = "10s" + id_attribute = "uid" + first_name_attribute = "firstname" + last_name_attribute = "lastname" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `base_dn` (String) Base DN for LDAP connections +- `bind_dn` (String) Bind DN for LDAP connections +- `bind_password` (String, Sensitive) Bind password for LDAP connections +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account +- `servers` (List of String) Servers to try in order for establishing LDAP connections +- `start_tls` (Boolean) Wether to use StartTLS for LDAP connections +- `timeout` (String) Timeout for LDAP connections +- `user_base` (String) User base for LDAP connections +- `user_filters` (Set of String) User filters for LDAP connections +- `user_object_classes` (Set of String) User object classes for LDAP connections + +### Optional + +- `avatar_url_attribute` (String) User attribute for the avatar url +- `display_name_attribute` (String) User attribute for the display name +- `email_attribute` (String) User attribute for the email +- `email_verified_attribute` (String) User attribute for the email verified state +- `first_name_attribute` (String) User attribute for the first name +- `id_attribute` (String) User attribute for the id +- `last_name_attribute` (String) User attribute for the last name +- `name` (String) Name of the IDP +- `nick_name_attribute` (String) User attribute for the nick name +- `phone_attribute` (String) User attribute for the phone +- `phone_verified_attribute` (String) User attribute for the phone verified state +- `preferred_language_attribute` (String) User attribute for the preferred language +- `preferred_username_attribute` (String) User attribute for the preferred username +- `profile_attribute` (String) User attribute for the profile + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import idp_ldap.imported '123456789012345678:b1nd_p4ssw0rd' +``` diff --git a/docs/resources/init_message_text.md b/docs/resources/init_message_text.md new file mode 100644 index 00000000..b59ec152 --- /dev/null +++ b/docs/resources/init_message_text.md @@ -0,0 +1,49 @@ +--- +page_title: "zitadel_init_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_init_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_init_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) +- `org_id` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/instance_member.md b/docs/resources/instance_member.md new file mode 100644 index 00000000..44ea5f0c --- /dev/null +++ b/docs/resources/instance_member.md @@ -0,0 +1,38 @@ +--- +page_title: "zitadel_instance_member Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the membership of a user on an instance, defined with the given role. +--- + +# zitadel_instance_member (Resource) + +Resource representing the membership of a user on an instance, defined with the given role. + +## Example Usage + +```terraform +resource "zitadel_instance_member" "default" { + user_id = data.zitadel_human_user.default.id + roles = ["IAM_OWNER"] +} +``` + + +## Schema + +### Required + +- `roles` (Set of String) List of roles granted, full list available here: https://zitadel.com/docs/guides/manage/console/managers#roles +- `user_id` (String) ID of the user + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import instance_member.imported '123456789012345678' +``` diff --git a/docs/resources/label_policy.md b/docs/resources/label_policy.md new file mode 100644 index 00000000..2e449d20 --- /dev/null +++ b/docs/resources/label_policy.md @@ -0,0 +1,86 @@ +--- +page_title: "zitadel_label_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the custom label policy of an organization. +--- + +# zitadel_label_policy (Resource) + +Resource representing the custom label policy of an organization. + +## Example Usage + +```terraform +resource "zitadel_label_policy" "default" { + org_id = data.zitadel_org.default.id + primary_color = "#5469d4" + hide_login_name_suffix = true + warn_color = "#cd3d56" + background_color = "#fafafa" + font_color = "#000000" + primary_color_dark = "#a5b4fc" + background_color_dark = "#111827" + warn_color_dark = "#ff3b5b" + font_color_dark = "#ffffff" + disable_watermark = false + set_active = true + logo_hash = filemd5("/path/to/logo.jpg") + logo_path = "/path/to/logo.jpg" + logo_dark_hash = filemd5("/path/to/logo_dark.jpg") + logo_dark_path = "/path/to/logo_dark.jpg" + icon_hash = filemd5("/path/to/icon.jpg") + icon_path = "/path/to/icon.jpg" + icon_dark_hash = filemd5("/path/to/icon_dark.jpg") + icon_dark_path = "/path/to/icon_dark.jpg" + font_hash = filemd5("/path/to/font.tff") + font_path = "/path/to/font.tff" +} +``` + + +## Schema + +### Required + +- `background_color` (String) hex value for background color +- `background_color_dark` (String) hex value for background color dark theme +- `disable_watermark` (Boolean) disable watermark +- `font_color` (String) hex value for font color +- `font_color_dark` (String) hex value for font color dark theme +- `hide_login_name_suffix` (Boolean) hides the org suffix on the login form if the scope "urn:zitadel:iam:org:domain:primary:{domainname}" is set. Details about this scope in https://zitadel.com/docs/apis/openidoauth/scopes#reserved-scopes +- `primary_color` (String) hex value for primary color +- `primary_color_dark` (String) hex value for primary color dark theme +- `warn_color` (String) hex value for warn color +- `warn_color_dark` (String) hex value for warn color dark theme + +### Optional + +- `font_hash` (String) +- `font_path` (String) +- `icon_dark_hash` (String) +- `icon_dark_path` (String) +- `icon_hash` (String) +- `icon_path` (String) +- `logo_dark_hash` (String) +- `logo_dark_path` (String) +- `logo_hash` (String) +- `logo_path` (String) +- `org_id` (String) ID of the organization +- `set_active` (Boolean) set the label policy active after creating/updating + +### Read-Only + +- `font_url` (String) +- `icon_url` (String) +- `icon_url_dark` (String) +- `id` (String) The ID of this resource. +- `logo_url` (String) +- `logo_url_dark` (String) + +## Import + +```terraform +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import label_policy.imported '123456789012345678' +``` diff --git a/docs/resources/lockout_policy.md b/docs/resources/lockout_policy.md new file mode 100644 index 00000000..789b141c --- /dev/null +++ b/docs/resources/lockout_policy.md @@ -0,0 +1,41 @@ +--- +page_title: "zitadel_lockout_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the custom lockout policy of an organization. +--- + +# zitadel_lockout_policy (Resource) + +Resource representing the custom lockout policy of an organization. + +## Example Usage + +```terraform +resource "zitadel_lockout_policy" "default" { + org_id = data.zitadel_org.default.id + max_password_attempts = "5" +} +``` + + +## Schema + +### Required + +- `max_password_attempts` (Number) Maximum password check attempts before the account gets locked. Attempts are reset as soon as the password is entered correct or the password is reset. + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import lockout_policy.imported '123456789012345678' +``` diff --git a/docs/resources/login_policy.md b/docs/resources/login_policy.md new file mode 100644 index 00000000..3baff4f2 --- /dev/null +++ b/docs/resources/login_policy.md @@ -0,0 +1,79 @@ +--- +page_title: "zitadel_login_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the custom login policy of an organization. +--- + +# zitadel_login_policy (Resource) + +Resource representing the custom login policy of an organization. + +## Example Usage + +```terraform +resource "zitadel_login_policy" "default" { + org_id = data.zitadel_org.default.id + user_login = true + allow_register = true + allow_external_idp = true + force_mfa = false + force_mfa_local_only = false + passwordless_type = "PASSWORDLESS_TYPE_ALLOWED" + hide_password_reset = "false" + password_check_lifetime = "240h0m0s" + external_login_check_lifetime = "240h0m0s" + multi_factor_check_lifetime = "24h0m0s" + mfa_init_skip_lifetime = "720h0m0s" + second_factor_check_lifetime = "24h0m0s" + ignore_unknown_usernames = true + default_redirect_uri = "localhost:8080" + second_factors = ["SECOND_FACTOR_TYPE_OTP", "SECOND_FACTOR_TYPE_U2F"] + multi_factors = ["MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION"] + idps = [data.zitadel_idp_google.default.id, data.zitadel_idp_azure_ad.default.id] + allow_domain_discovery = true + disable_login_with_email = true + disable_login_with_phone = true +} +``` + + +## Schema + +### Required + +- `allow_external_idp` (Boolean) defines if a user is allowed to add a defined identity provider. E.g. Google auth +- `allow_register` (Boolean) defines if a person is allowed to register a user on this organisation +- `default_redirect_uri` (String) defines where the user will be redirected to if the login is started without app context (e.g. from mail) +- `external_login_check_lifetime` (String) +- `force_mfa` (Boolean) defines if a user MUST use a multi factor to log in +- `force_mfa_local_only` (Boolean) if activated, ZITADEL only enforces MFA on local authentications. On authentications through MFA, ZITADEL won't prompt for MFA. +- `hide_password_reset` (Boolean) defines if password reset link should be shown in the login screen +- `ignore_unknown_usernames` (Boolean) defines if unknown username on login screen directly return an error or always display the password screen +- `mfa_init_skip_lifetime` (String) +- `multi_factor_check_lifetime` (String) +- `password_check_lifetime` (String) +- `passwordless_type` (String) defines if passwordless is allowed for users +- `second_factor_check_lifetime` (String) +- `user_login` (Boolean) defines if a user is allowed to login with his username and password + +### Optional + +- `allow_domain_discovery` (Boolean) if set to true, the suffix (@domain.com) of an unknown username input on the login screen will be matched against the org domains and will redirect to the registration of that organisation on success. +- `disable_login_with_email` (Boolean) defines if user can additionally (to the loginname) be identified by their verified email address +- `disable_login_with_phone` (Boolean) defines if user can additionally (to the loginname) be identified by their verified phone number +- `idps` (Set of String) allowed idps to login or register +- `multi_factors` (Set of String) allowed multi factors +- `org_id` (String) ID of the organization +- `second_factors` (Set of String) allowed second factors + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import login_policy.imported '123456789012345678' +``` diff --git a/docs/resources/login_texts.md b/docs/resources/login_texts.md new file mode 100644 index 00000000..2cc7f35e --- /dev/null +++ b/docs/resources/login_texts.md @@ -0,0 +1,811 @@ +--- +page_title: "zitadel_login_texts Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_login_texts (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_login_texts" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + email_verification_done_text = { + cancel_button_text = "example" + description = "example" + login_button_text = "example" + next_button_text = "example" + title = "example" + } + email_verification_text = { + code_label = "example" + description = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + external_registration_user_overview_text = { + back_button_text = "example" + description = "example" + email_label = "example" + firstname_label = "example" + language_label = "example" + lastname_label = "example" + next_button_text = "example" + nickname_label = "example" + phone_label = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + external_user_not_found_text = { + auto_register_button_text = "example" + description = "example" + link_button_text = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + } + footer_text = { + help = "example" + privacy_policy = "example" + tos = "example" + } + init_mfa_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + init_mfa_otp_text = { + cancel_button_text = "example" + code_label = "example" + description = "example" + description_otp = "example" + next_button_text = "example" + secret_label = "example" + title = "example" + } + init_mfa_prompt_text = { + description = "example" + next_button_text = "example" + otp_option = "example" + skip_button_text = "example" + title = "example" + u2f_option = "example" + } + init_mfa_u2f_text = { + description = "example" + error_retry = "example" + not_supported = "example" + register_token_button_text = "example" + title = "example" + token_name_label = "example" + } + init_password_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + init_password_text = { + code_label = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + initialize_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + initialize_user_text = { + code_label = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + linking_user_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + login_text = { + description = "example" + description_linking_process = "example" + external_user_description = "example" + login_name_label = "example" + login_name_placeholder = "example" + next_button_text = "example" + register_button_text = "example" + title = "example" + title_linking_process = "example" + user_must_be_member_of_org = "example" + user_name_placeholder = "example" + } + logout_text = { + description = "example" + login_button_text = "example" + title = "example" + } + mfa_providers_text = { + choose_other = "example" + otp = "example" + u2f = "example" + } + password_change_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + password_change_text = { + cancel_button_text = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + old_password_label = "example" + title = "example" + } + password_reset_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + password_text = { + back_button_text = "example" + confirmation = "example" + description = "example" + has_lowercase = "example" + has_number = "example" + has_symbol = "example" + has_uppercase = "example" + min_length = "example" + next_button_text = "example" + password_label = "example" + reset_link_text = "example" + title = "example" + } + passwordless_prompt_text = { + description = "example" + description_init = "example" + next_button_text = "example" + passwordless_button_text = "example" + skip_button_text = "example" + title = "example" + } + passwordless_registration_done_text = { + cancel_button_text = "example" + description = "example" + description_close = "example" + next_button_text = "example" + title = "example" + } + passwordless_registration_text = { + description = "example" + error_retry = "example" + not_supported = "example" + register_token_button_text = "example" + title = "example" + token_name_label = "example" + } + passwordless_text = { + description = "example" + error_retry = "example" + login_with_pw_button_text = "example" + not_supported = "example" + title = "example" + validate_token_button_text = "example" + } + registration_option_text = { + description = "example" + external_login_description = "example" + title = "example" + user_name_button_text = "example" + } + registration_org_text = { + description = "example" + email_label = "example" + firstname_label = "example" + lastname_label = "example" + orgname_label = "example" + password_confirm_label = "example" + password_label = "example" + privacy_link_text = "example" + save_button_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + registration_user_text = { + back_button_text = "example" + description = "example" + description_org_register = "example" + email_label = "example" + firstname_label = "example" + gender_label = "example" + language_label = "example" + lastname_label = "example" + next_button_text = "example" + password_confirm_label = "example" + password_label = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + select_account_text = { + description = "example" + description_linking_process = "example" + other_user = "example" + session_state_active = "example" + session_state_inactive = "example" + title = "example" + title_linking_process = "example" + user_must_be_member_of_org = "example" + } + success_login_text = { + auto_redirect_description = "example" + next_button_text = "example" + redirected_description = "example" + title = "example" + } + username_change_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + username_change_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + username_label = "example" + } + verify_mfa_otp_text = { + code_label = "example" + description = "example" + next_button_text = "example" + title = "example" + } + verify_mfa_u2f_text = { + description = "example" + error_retry = "example" + not_supported = "example" + title = "example" + validate_token_text = "example" + } +} +``` + + +## Schema + +### Required + +- `language` (String) +- `org_id` (String) + +### Optional + +- `email_verification_done_text` (Attributes) (see [below for nested schema](#nestedatt--email_verification_done_text)) +- `email_verification_text` (Attributes) (see [below for nested schema](#nestedatt--email_verification_text)) +- `external_registration_user_overview_text` (Attributes) (see [below for nested schema](#nestedatt--external_registration_user_overview_text)) +- `external_user_not_found_text` (Attributes) (see [below for nested schema](#nestedatt--external_user_not_found_text)) +- `footer_text` (Attributes) (see [below for nested schema](#nestedatt--footer_text)) +- `init_mfa_done_text` (Attributes) (see [below for nested schema](#nestedatt--init_mfa_done_text)) +- `init_mfa_otp_text` (Attributes) (see [below for nested schema](#nestedatt--init_mfa_otp_text)) +- `init_mfa_prompt_text` (Attributes) (see [below for nested schema](#nestedatt--init_mfa_prompt_text)) +- `init_mfa_u2f_text` (Attributes) (see [below for nested schema](#nestedatt--init_mfa_u2f_text)) +- `init_password_done_text` (Attributes) (see [below for nested schema](#nestedatt--init_password_done_text)) +- `init_password_text` (Attributes) (see [below for nested schema](#nestedatt--init_password_text)) +- `initialize_done_text` (Attributes) (see [below for nested schema](#nestedatt--initialize_done_text)) +- `initialize_user_text` (Attributes) (see [below for nested schema](#nestedatt--initialize_user_text)) +- `linking_user_done_text` (Attributes) (see [below for nested schema](#nestedatt--linking_user_done_text)) +- `login_text` (Attributes) (see [below for nested schema](#nestedatt--login_text)) +- `logout_text` (Attributes) (see [below for nested schema](#nestedatt--logout_text)) +- `mfa_providers_text` (Attributes) (see [below for nested schema](#nestedatt--mfa_providers_text)) +- `password_change_done_text` (Attributes) (see [below for nested schema](#nestedatt--password_change_done_text)) +- `password_change_text` (Attributes) (see [below for nested schema](#nestedatt--password_change_text)) +- `password_reset_done_text` (Attributes) (see [below for nested schema](#nestedatt--password_reset_done_text)) +- `password_text` (Attributes) (see [below for nested schema](#nestedatt--password_text)) +- `passwordless_prompt_text` (Attributes) (see [below for nested schema](#nestedatt--passwordless_prompt_text)) +- `passwordless_registration_done_text` (Attributes) (see [below for nested schema](#nestedatt--passwordless_registration_done_text)) +- `passwordless_registration_text` (Attributes) (see [below for nested schema](#nestedatt--passwordless_registration_text)) +- `passwordless_text` (Attributes) (see [below for nested schema](#nestedatt--passwordless_text)) +- `registration_option_text` (Attributes) (see [below for nested schema](#nestedatt--registration_option_text)) +- `registration_org_text` (Attributes) (see [below for nested schema](#nestedatt--registration_org_text)) +- `registration_user_text` (Attributes) (see [below for nested schema](#nestedatt--registration_user_text)) +- `select_account_text` (Attributes) (see [below for nested schema](#nestedatt--select_account_text)) +- `success_login_text` (Attributes) (see [below for nested schema](#nestedatt--success_login_text)) +- `username_change_done_text` (Attributes) (see [below for nested schema](#nestedatt--username_change_done_text)) +- `username_change_text` (Attributes) (see [below for nested schema](#nestedatt--username_change_text)) +- `verify_mfa_otp_text` (Attributes) (see [below for nested schema](#nestedatt--verify_mfa_otp_text)) +- `verify_mfa_u2f_text` (Attributes) (see [below for nested schema](#nestedatt--verify_mfa_u2f_text)) + +### Read-Only + +- `id` (String) The ID of this resource. + + +### Nested Schema for `email_verification_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `login_button_text` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `email_verification_text` + +Optional: + +- `code_label` (String) +- `description` (String) +- `next_button_text` (String) +- `resend_button_text` (String) +- `title` (String) + + + +### Nested Schema for `external_registration_user_overview_text` + +Optional: + +- `back_button_text` (String) +- `description` (String) +- `email_label` (String) +- `firstname_label` (String) +- `language_label` (String) +- `lastname_label` (String) +- `next_button_text` (String) +- `nickname_label` (String) +- `phone_label` (String) +- `privacy_confirm` (String) +- `privacy_link_text` (String) +- `title` (String) +- `tos_and_privacy_label` (String) +- `tos_confirm` (String) +- `tos_link_text` (String) +- `username_label` (String) + + + +### Nested Schema for `external_user_not_found_text` + +Optional: + +- `auto_register_button_text` (String) +- `description` (String) +- `link_button_text` (String) +- `privacy_confirm` (String) +- `privacy_link_text` (String) +- `title` (String) +- `tos_and_privacy_label` (String) +- `tos_confirm` (String) +- `tos_link_text` (String) + + + +### Nested Schema for `footer_text` + +Optional: + +- `help` (String) +- `privacy_policy` (String) +- `tos` (String) + + + +### Nested Schema for `init_mfa_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `init_mfa_otp_text` + +Optional: + +- `cancel_button_text` (String) +- `code_label` (String) +- `description` (String) +- `description_otp` (String) +- `next_button_text` (String) +- `secret_label` (String) +- `title` (String) + + + +### Nested Schema for `init_mfa_prompt_text` + +Optional: + +- `description` (String) +- `next_button_text` (String) +- `otp_option` (String) +- `skip_button_text` (String) +- `title` (String) +- `u2f_option` (String) + + + +### Nested Schema for `init_mfa_u2f_text` + +Optional: + +- `description` (String) +- `error_retry` (String) +- `not_supported` (String) +- `register_token_button_text` (String) +- `title` (String) +- `token_name_label` (String) + + + +### Nested Schema for `init_password_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `init_password_text` + +Optional: + +- `code_label` (String) +- `description` (String) +- `new_password_confirm_label` (String) +- `new_password_label` (String) +- `next_button_text` (String) +- `resend_button_text` (String) +- `title` (String) + + + +### Nested Schema for `initialize_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `initialize_user_text` + +Optional: + +- `code_label` (String) +- `description` (String) +- `new_password_confirm_label` (String) +- `new_password_label` (String) +- `next_button_text` (String) +- `resend_button_text` (String) +- `title` (String) + + + +### Nested Schema for `linking_user_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `login_text` + +Optional: + +- `description` (String) +- `description_linking_process` (String) +- `external_user_description` (String) +- `login_name_label` (String) +- `login_name_placeholder` (String) +- `next_button_text` (String) +- `register_button_text` (String) +- `title` (String) +- `title_linking_process` (String) +- `user_must_be_member_of_org` (String) +- `user_name_placeholder` (String) + + + +### Nested Schema for `logout_text` + +Optional: + +- `description` (String) +- `login_button_text` (String) +- `title` (String) + + + +### Nested Schema for `mfa_providers_text` + +Optional: + +- `choose_other` (String) +- `otp` (String) +- `u2f` (String) + + + +### Nested Schema for `password_change_done_text` + +Optional: + +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `password_change_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `new_password_confirm_label` (String) +- `new_password_label` (String) +- `next_button_text` (String) +- `old_password_label` (String) +- `title` (String) + + + +### Nested Schema for `password_reset_done_text` + +Optional: + +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `password_text` + +Optional: + +- `back_button_text` (String) +- `confirmation` (String) +- `description` (String) +- `has_lowercase` (String) +- `has_number` (String) +- `has_symbol` (String) +- `has_uppercase` (String) +- `min_length` (String) +- `next_button_text` (String) +- `password_label` (String) +- `reset_link_text` (String) +- `title` (String) + + + +### Nested Schema for `passwordless_prompt_text` + +Optional: + +- `description` (String) +- `description_init` (String) +- `next_button_text` (String) +- `passwordless_button_text` (String) +- `skip_button_text` (String) +- `title` (String) + + + +### Nested Schema for `passwordless_registration_done_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `description_close` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `passwordless_registration_text` + +Optional: + +- `description` (String) +- `error_retry` (String) +- `not_supported` (String) +- `register_token_button_text` (String) +- `title` (String) +- `token_name_label` (String) + + + +### Nested Schema for `passwordless_text` + +Optional: + +- `description` (String) +- `error_retry` (String) +- `login_with_pw_button_text` (String) +- `not_supported` (String) +- `title` (String) +- `validate_token_button_text` (String) + + + +### Nested Schema for `registration_option_text` + +Optional: + +- `description` (String) +- `external_login_description` (String) +- `login_button_text` (String) +- `title` (String) +- `user_name_button_text` (String) + + + +### Nested Schema for `registration_org_text` + +Optional: + +- `description` (String) +- `email_label` (String) +- `firstname_label` (String) +- `lastname_label` (String) +- `orgname_label` (String) +- `password_confirm_label` (String) +- `password_label` (String) +- `privacy_confirm` (String) +- `privacy_link_text` (String) +- `save_button_text` (String) +- `title` (String) +- `tos_and_privacy_label` (String) +- `tos_confirm` (String) +- `tos_link_text` (String) +- `username_label` (String) + + + +### Nested Schema for `registration_user_text` + +Optional: + +- `back_button_text` (String) +- `description` (String) +- `description_org_register` (String) +- `email_label` (String) +- `firstname_label` (String) +- `gender_label` (String) +- `language_label` (String) +- `lastname_label` (String) +- `next_button_text` (String) +- `password_confirm_label` (String) +- `password_label` (String) +- `privacy_confirm` (String) +- `privacy_link_text` (String) +- `title` (String) +- `tos_and_privacy_label` (String) +- `tos_confirm` (String) +- `tos_link_text` (String) +- `username_label` (String) + + + +### Nested Schema for `select_account_text` + +Optional: + +- `description` (String) +- `description_linking_process` (String) +- `other_user` (String) +- `session_state_active` (String) +- `session_state_inactive` (String) +- `title` (String) +- `title_linking_process` (String) +- `user_must_be_member_of_org` (String) + + + +### Nested Schema for `success_login_text` + +Optional: + +- `auto_redirect_description` (String) Text to describe that auto-redirect should happen after successful login +- `next_button_text` (String) +- `redirected_description` (String) Text to describe that the window can be closed after redirect +- `title` (String) + + + +### Nested Schema for `username_change_done_text` + +Optional: + +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `username_change_text` + +Optional: + +- `cancel_button_text` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) +- `username_label` (String) + + + +### Nested Schema for `verify_mfa_otp_text` + +Optional: + +- `code_label` (String) +- `description` (String) +- `next_button_text` (String) +- `title` (String) + + + +### Nested Schema for `verify_mfa_u2f_text` + +Optional: + +- `description` (String) +- `error_retry` (String) +- `not_supported` (String) +- `title` (String) +- `validate_token_text` (String) \ No newline at end of file diff --git a/docs/resources/machine_key.md b/docs/resources/machine_key.md new file mode 100644 index 00000000..e4f2b351 --- /dev/null +++ b/docs/resources/machine_key.md @@ -0,0 +1,46 @@ +--- +page_title: "zitadel_machine_key Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a machine key +--- + +# zitadel_machine_key (Resource) + +Resource representing a machine key + +## Example Usage + +```terraform +resource "zitadel_machine_key" "default" { + org_id = data.zitadel_org.default.id + user_id = data.zitadel_machine_user.default.id + key_type = "KEY_TYPE_JSON" + expiration_date = "2519-04-01T08:45:00Z" +} +``` + + +## Schema + +### Required + +- `key_type` (String) Type of the machine key, supported values: KEY_TYPE_UNSPECIFIED, KEY_TYPE_JSON +- `user_id` (String) ID of the user + +### Optional + +- `expiration_date` (String) Expiration date of the machine key in the RFC3339 format +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. +- `key_details` (String, Sensitive) Value of the machine key + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import machine_key.imported '123456789012345678:123456789012345678:123456789012345678:{"type":"serviceaccount","keyId":"123456789012345678","key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpQ...-----END RSA PRIVATE KEY-----\n","userId":"123456789012345678"}' +``` diff --git a/docs/resources/machine_user.md b/docs/resources/machine_user.md new file mode 100644 index 00000000..eb9541af --- /dev/null +++ b/docs/resources/machine_user.md @@ -0,0 +1,49 @@ +--- +page_title: "zitadel_machine_user Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a serviceaccount situated under an organization, which then can be authorized through memberships or direct grants on other resources. +--- + +# zitadel_machine_user (Resource) + +Resource representing a serviceaccount situated under an organization, which then can be authorized through memberships or direct grants on other resources. + +## Example Usage + +```terraform +resource "zitadel_machine_user" "default" { + org_id = data.zitadel_org.default.id + user_name = "machine@example.com" + name = "name" + description = "a machine user" +} +``` + + +## Schema + +### Required + +- `name` (String) Name of the machine user +- `user_name` (String) Username + +### Optional + +- `access_token_type` (String) Access token type, supported values: ACCESS_TOKEN_TYPE_BEARER, ACCESS_TOKEN_TYPE_JWT +- `description` (String) Description of the user +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. +- `login_names` (List of String) Loginnames +- `preferred_login_name` (String) Preferred login name +- `state` (String) State of the user + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import machine_user.imported '123456789012345678:123456789012345678' +``` diff --git a/docs/resources/notification_policy.md b/docs/resources/notification_policy.md new file mode 100644 index 00000000..49a05cf2 --- /dev/null +++ b/docs/resources/notification_policy.md @@ -0,0 +1,41 @@ +--- +page_title: "zitadel_notification_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the custom notification policy of an organization. +--- + +# zitadel_notification_policy (Resource) + +Resource representing the custom notification policy of an organization. + +## Example Usage + +```terraform +resource "zitadel_notification_policy" "default" { + org_id = data.zitadel_org.default.id + password_change = false +} +``` + + +## Schema + +### Required + +- `password_change` (Boolean) Send notification if a user changes his password + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import notification_policy.imported '123456789012345678' +``` diff --git a/docs/resources/org.md b/docs/resources/org.md new file mode 100644 index 00000000..0cebcd7e --- /dev/null +++ b/docs/resources/org.md @@ -0,0 +1,38 @@ +--- +page_title: "zitadel_org Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing an organization in ZITADEL, which is the highest level after the instance and contains several other resource including policies if the configuration differs to the default policies on the instance. +--- + +# zitadel_org (Resource) + +Resource representing an organization in ZITADEL, which is the highest level after the instance and contains several other resource including policies if the configuration differs to the default policies on the instance. + +## Example Usage + +```terraform +resource "zitadel_org" "default" { + name = "terraform-test" +} +``` + + +## Schema + +### Required + +- `name` (String) Name of the org + +### Read-Only + +- `id` (String) The ID of this resource. +- `primary_domain` (String) Primary domain of the org +- `state` (String) State of the org + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import org.imported '123456789012345678' +``` diff --git a/docs/resources/org_idp_azure_ad.md b/docs/resources/org_idp_azure_ad.md new file mode 100644 index 00000000..4fb57bf5 --- /dev/null +++ b/docs/resources/org_idp_azure_ad.md @@ -0,0 +1,60 @@ +--- +page_title: "zitadel_org_idp_azure_ad Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing an Azure AD IdP on the organization. +--- + +# zitadel_org_idp_azure_ad (Resource) + +Resource representing an Azure AD IdP on the organization. + +## Example Usage + +```terraform +resource "zitadel_org_idp_azure_ad" "default" { + org_id = data.zitadel_org.default.id + name = "Azure AD" + client_id = "9065bfc8-a08a..." + client_secret = "H2n***" + scopes = ["openid", "profile", "email", "User.Read"] + tenant_type = "AZURE_AD_TENANT_TYPE_ORGANISATIONS" + email_verified = true + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `email_verified` (Boolean) automatically mark emails as verified +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account + +### Optional + +- `name` (String) Name of the IDP +- `org_id` (String) ID of the organization +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider +- `tenant_id` (String) if tenant_id is not set, the tenant_type is used +- `tenant_type` (String) the azure ad tenant type + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_azure_ad.imported '123456789012345678:123456789012345678:12345678-1234-1234-1234-123456789012' +``` diff --git a/docs/resources/org_idp_github.md b/docs/resources/org_idp_github.md new file mode 100644 index 00000000..b2334f41 --- /dev/null +++ b/docs/resources/org_idp_github.md @@ -0,0 +1,55 @@ +--- +page_title: "zitadel_org_idp_github Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a GitHub IdP on the organization. +--- + +# zitadel_org_idp_github (Resource) + +Resource representing a GitHub IdP on the organization. + +## Example Usage + +```terraform +resource "zitadel_org_idp_github" "default" { + org_id = data.zitadel_org.default.id + name = "GitHub" + client_id = "86a165..." + client_secret = "*****afdbac18" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account + +### Optional + +- `name` (String) Name of the IDP +- `org_id` (String) ID of the organization +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_github.imported '123456789012345678:123456789012345678:1234567890123456781234567890123456787890' +``` diff --git a/docs/resources/org_idp_github_es.md b/docs/resources/org_idp_github_es.md new file mode 100644 index 00000000..dbce8df2 --- /dev/null +++ b/docs/resources/org_idp_github_es.md @@ -0,0 +1,61 @@ +--- +page_title: "zitadel_org_idp_github_es Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a GitHub Enterprise IdP on the organization. +--- + +# zitadel_org_idp_github_es (Resource) + +Resource representing a GitHub Enterprise IdP on the organization. + +## Example Usage + +```terraform +resource "zitadel_org_idp_github_es" "default" { + org_id = data.zitadel_org.default.id + name = "GitHub Enterprise Server" + client_id = "86a165..." + client_secret = "*****afdbac18" + scopes = ["openid", "profile", "email"] + authorization_endpoint = "https://auth.endpoint" + token_endpoint = "https://token.endpoint" + user_endpoint = "https://user.endpoint" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `authorization_endpoint` (String) the providers authorization endpoint +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account +- `token_endpoint` (String) the providers token endpoint +- `user_endpoint` (String) the providers user endpoint + +### Optional + +- `name` (String) Name of the IDP +- `org_id` (String) ID of the organization +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_github_es.imported '123456789012345678:123456789012345678:123456789012345678:123456789012345678' +``` diff --git a/docs/resources/org_idp_gitlab.md b/docs/resources/org_idp_gitlab.md new file mode 100644 index 00000000..60d5c446 --- /dev/null +++ b/docs/resources/org_idp_gitlab.md @@ -0,0 +1,55 @@ +--- +page_title: "zitadel_org_idp_gitlab Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a GitLab IdP on the organization. +--- + +# zitadel_org_idp_gitlab (Resource) + +Resource representing a GitLab IdP on the organization. + +## Example Usage + +```terraform +resource "zitadel_org_idp_gitlab" "default" { + org_id = data.zitadel_org.default.id + name = "GitLab" + client_id = "15765e..." + client_secret = "*****abcxyz" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account + +### Optional + +- `name` (String) Name of the IDP +- `org_id` (String) ID of the organization +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_gitlab.imported '123456789012345678:123456789012345678:1234567890abcdef' +``` diff --git a/docs/resources/org_idp_gitlab_self_hosted.md b/docs/resources/org_idp_gitlab_self_hosted.md new file mode 100644 index 00000000..dee3475c --- /dev/null +++ b/docs/resources/org_idp_gitlab_self_hosted.md @@ -0,0 +1,57 @@ +--- +page_title: "zitadel_org_idp_gitlab_self_hosted Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a GitLab Self Hosted IdP on the organization. +--- + +# zitadel_org_idp_gitlab_self_hosted (Resource) + +Resource representing a GitLab Self Hosted IdP on the organization. + +## Example Usage + +```terraform +resource "zitadel_org_idp_gitlab_self_hosted" "default" { + org_id = data.zitadel_org.default.id + name = "GitLab Self Hosted" + client_id = "15765e..." + client_secret = "*****abcxyz" + scopes = ["openid", "profile", "email"] + issuer = "https://my.issuer" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account +- `issuer` (String) the providers issuer + +### Optional + +- `name` (String) Name of the IDP +- `org_id` (String) ID of the organization +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_gitlab_self_hosted.imported '123456789012345678:123456789012345678:1234567890abcdef' +``` diff --git a/docs/resources/org_idp_google.md b/docs/resources/org_idp_google.md new file mode 100644 index 00000000..f080a840 --- /dev/null +++ b/docs/resources/org_idp_google.md @@ -0,0 +1,55 @@ +--- +page_title: "zitadel_org_idp_google Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a Google IdP on the organization. +--- + +# zitadel_org_idp_google (Resource) + +Resource representing a Google IdP on the organization. + +## Example Usage + +```terraform +resource "zitadel_org_idp_google" "default" { + org_id = data.zitadel_org.default.id + name = "Google" + client_id = "182902..." + client_secret = "GOCSPX-*****" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account + +### Optional + +- `name` (String) Name of the IDP +- `org_id` (String) ID of the organization +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_google.imported '123456789012345678:123456789012345678:G1234567890123' +``` diff --git a/docs/resources/org_idp_jwt.md b/docs/resources/org_idp_jwt.md new file mode 100644 index 00000000..a8220923 --- /dev/null +++ b/docs/resources/org_idp_jwt.md @@ -0,0 +1,53 @@ +--- +page_title: "zitadel_org_idp_jwt Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a generic JWT IdP of the organization. +--- + +# zitadel_org_idp_jwt (Resource) + +Resource representing a generic JWT IdP of the organization. + +## Example Usage + +```terraform +resource "zitadel_org_idp_jwt" "default" { + org_id = data.zitadel_org.default.id + name = "jwtidp" + styling_type = "STYLING_TYPE_UNSPECIFIED" + jwt_endpoint = "https://jwtendpoint.com/jwt" + issuer = "https://google.com" + keys_endpoint = "https://jwtendpoint.com/keys" + header_name = "x-auth-token" + auto_register = false +} +``` + + +## Schema + +### Required + +- `auto_register` (Boolean) auto register for users from this idp +- `header_name` (String) the name of the header where the JWT is sent in, default is authorization +- `issuer` (String) the issuer of the jwt (for validation) +- `jwt_endpoint` (String) the endpoint where the jwt can be extracted +- `keys_endpoint` (String) the endpoint to the key (JWK) which are used to sign the JWT with +- `name` (String) Name of the IDP +- `styling_type` (String) Some identity providers specify the styling of the button to their login, supported values: STYLING_TYPE_UNSPECIFIED, STYLING_TYPE_GOOGLE + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_jwt.imported '123456789012345678:123456789012345678' +``` diff --git a/docs/resources/org_idp_ldap.md b/docs/resources/org_idp_ldap.md new file mode 100644 index 00000000..19309bfe --- /dev/null +++ b/docs/resources/org_idp_ldap.md @@ -0,0 +1,83 @@ +--- +page_title: "zitadel_org_idp_ldap Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing an LDAP IdP on the organization. +--- + +# zitadel_org_idp_ldap (Resource) + +Resource representing an LDAP IdP on the organization. + +## Example Usage + +```terraform +resource "zitadel_org_idp_ldap" "default" { + org_id = data.zitadel_org.default.id + name = "LDAP" + servers = ["ldaps://my.primary.server:389", "ldaps://my.secondary.server:389"] + start_tls = false + base_dn = "dc=example,dc=com" + bind_dn = "cn=admin,dc=example,dc=com" + bind_password = "Password1!" + user_base = "dn" + user_object_classes = ["inetOrgPerson"] + user_filters = ["uid", "email"] + timeout = "10s" + id_attribute = "uid" + first_name_attribute = "firstname" + last_name_attribute = "lastname" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} +``` + + +## Schema + +### Required + +- `base_dn` (String) Base DN for LDAP connections +- `bind_dn` (String) Bind DN for LDAP connections +- `bind_password` (String, Sensitive) Bind password for LDAP connections +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account +- `servers` (List of String) Servers to try in order for establishing LDAP connections +- `start_tls` (Boolean) Wether to use StartTLS for LDAP connections +- `timeout` (String) Timeout for LDAP connections +- `user_base` (String) User base for LDAP connections +- `user_filters` (Set of String) User filters for LDAP connections +- `user_object_classes` (Set of String) User object classes for LDAP connections + +### Optional + +- `avatar_url_attribute` (String) User attribute for the avatar url +- `display_name_attribute` (String) User attribute for the display name +- `email_attribute` (String) User attribute for the email +- `email_verified_attribute` (String) User attribute for the email verified state +- `first_name_attribute` (String) User attribute for the first name +- `id_attribute` (String) User attribute for the id +- `last_name_attribute` (String) User attribute for the last name +- `name` (String) Name of the IDP +- `nick_name_attribute` (String) User attribute for the nick name +- `org_id` (String) ID of the organization +- `phone_attribute` (String) User attribute for the phone +- `phone_verified_attribute` (String) User attribute for the phone verified state +- `preferred_language_attribute` (String) User attribute for the preferred language +- `preferred_username_attribute` (String) User attribute for the preferred username +- `profile_attribute` (String) User attribute for the profile + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_ldap.imported '123456789012345678:123456789012345678:b1nd_p4ssw0rd' +``` diff --git a/docs/resources/org_idp_oidc.md b/docs/resources/org_idp_oidc.md new file mode 100644 index 00000000..828e2e67 --- /dev/null +++ b/docs/resources/org_idp_oidc.md @@ -0,0 +1,59 @@ +--- +page_title: "zitadel_org_idp_oidc Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a generic OIDC IdP on the organization. +--- + +# zitadel_org_idp_oidc (Resource) + +Resource representing a generic OIDC IdP on the organization. + +## Example Usage + +```terraform +resource "zitadel_org_idp_oidc" "default" { + org_id = data.zitadel_org.default.id + name = "My Generic OIDC IDP" + client_id = "a_client_id" + client_secret = "a_client_secret" + scopes = ["openid", "profile", "email"] + issuer = "https://example.com" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true + is_id_token_mapping = true +} +``` + + +## Schema + +### Required + +- `client_id` (String) client id generated by the identity provider +- `client_secret` (String, Sensitive) client secret generated by the identity provider +- `is_auto_creation` (Boolean) enable if a new account in ZITADEL should be created automatically on login with an external account +- `is_auto_update` (Boolean) enable if a the ZITADEL account fields should be updated automatically on each login +- `is_creation_allowed` (Boolean) enable if users should be able to create a new account in ZITADEL when using an external account +- `is_id_token_mapping` (Boolean) if true, provider information get mapped from the id token, not from the userinfo endpoint +- `is_linking_allowed` (Boolean) enable if users should be able to link an existing ZITADEL user with an external account +- `issuer` (String) the issuer of the idp + +### Optional + +- `name` (String) Name of the IDP +- `org_id` (String) ID of the organization +- `scopes` (Set of String) the scopes requested by ZITADEL during the request on the identity provider + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_oidc.imported '123456789012345678:123456789012345678:1234567890abcdef' +``` diff --git a/docs/resources/org_member.md b/docs/resources/org_member.md new file mode 100644 index 00000000..dcca6ade --- /dev/null +++ b/docs/resources/org_member.md @@ -0,0 +1,43 @@ +--- +page_title: "zitadel_org_member Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the membership of a user on an organization, defined with the given role. +--- + +# zitadel_org_member (Resource) + +Resource representing the membership of a user on an organization, defined with the given role. + +## Example Usage + +```terraform +resource "zitadel_org_member" "default" { + org_id = data.zitadel_org.default.id + user_id = data.zitadel_human_user.default.id + roles = ["ORG_OWNER"] +} +``` + + +## Schema + +### Required + +- `roles` (Set of String) List of roles granted +- `user_id` (String) ID of the user + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import org_member.imported '123456789012345678:123456789012345678' +``` diff --git a/docs/resources/password_change_message_text.md b/docs/resources/password_change_message_text.md new file mode 100644 index 00000000..2e0fb514 --- /dev/null +++ b/docs/resources/password_change_message_text.md @@ -0,0 +1,49 @@ +--- +page_title: "zitadel_password_change_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_password_change_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_password_change_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) +- `org_id` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. diff --git a/docs/resources/password_complexity_policy.md b/docs/resources/password_complexity_policy.md new file mode 100644 index 00000000..11069b79 --- /dev/null +++ b/docs/resources/password_complexity_policy.md @@ -0,0 +1,42 @@ +--- +page_title: "zitadel_password_complexity_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the custom password complexity policy of an organization. +--- + +# zitadel_password_complexity_policy (Resource) + +Resource representing the custom password complexity policy of an organization. + +## Example Usage + +```terraform +resource "zitadel_password_complexity_policy" "default" { + org_id = data.zitadel_org.default.id + min_length = "8" + has_uppercase = true + has_lowercase = true + has_number = true + has_symbol = true +} +``` + + +## Schema + +### Required + +- `has_lowercase` (Boolean) defines if the password MUST contain a lower case letter +- `has_number` (Boolean) defines if the password MUST contain a number +- `has_symbol` (Boolean) defines if the password MUST contain a symbol. E.g. "$" +- `has_uppercase` (Boolean) defines if the password MUST contain an upper case letter +- `min_length` (Number) Minimal length for the password + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/password_reset_message_text.md b/docs/resources/password_reset_message_text.md new file mode 100644 index 00000000..b5557b39 --- /dev/null +++ b/docs/resources/password_reset_message_text.md @@ -0,0 +1,49 @@ +--- +page_title: "zitadel_password_reset_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_password_reset_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_password_reset_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) +- `org_id` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/passwordless_registration_message_text.md b/docs/resources/passwordless_registration_message_text.md new file mode 100644 index 00000000..bad567d5 --- /dev/null +++ b/docs/resources/passwordless_registration_message_text.md @@ -0,0 +1,49 @@ +--- +page_title: "zitadel_passwordless_registration_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_passwordless_registration_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_passwordless_registration_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) +- `org_id` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/personal_access_token.md b/docs/resources/personal_access_token.md new file mode 100644 index 00000000..ceaf3ecd --- /dev/null +++ b/docs/resources/personal_access_token.md @@ -0,0 +1,44 @@ +--- +page_title: "zitadel_personal_access_token Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing a personal access token of a user +--- + +# zitadel_personal_access_token (Resource) + +Resource representing a personal access token of a user + +## Example Usage + +```terraform +resource "zitadel_personal_access_token" "default" { + org_id = data.zitadel_org.default.id + user_id = data.zitadel_machine_user.default.id + expiration_date = "2519-04-01T08:45:00Z" +} +``` + + +## Schema + +### Required + +- `user_id` (String) ID of the user + +### Optional + +- `expiration_date` (String) Expiration date of the token in the RFC3339 format +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. +- `token` (String, Sensitive) Value of the token + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import personal_access_token.imported '123456789012345678:123456789012345678:123456789012345678:LHt79...' +``` diff --git a/docs/resources/privacy_policy.md b/docs/resources/privacy_policy.md new file mode 100644 index 00000000..3dfd4b23 --- /dev/null +++ b/docs/resources/privacy_policy.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_privacy_policy Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the custom privacy policy of an organization. +--- + +# zitadel_privacy_policy (Resource) + +Resource representing the custom privacy policy of an organization. + +## Example Usage + +```terraform +resource "zitadel_privacy_policy" "default" { + org_id = data.zitadel_org.default.id + tos_link = "https://example.com/tos" + privacy_link = "https://example.com/privacy" + help_link = "https://example.com/help" + support_email = "support@example.com" +} +``` + + +## Schema + +### Required + +- `help_link` (String) +- `privacy_link` (String) +- `support_email` (String) +- `tos_link` (String) + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import privacy_policy.imported '123456789012345678' +``` diff --git a/docs/resources/project.md b/docs/resources/project.md new file mode 100644 index 00000000..5fa5cb62 --- /dev/null +++ b/docs/resources/project.md @@ -0,0 +1,50 @@ +--- +page_title: "zitadel_project Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the project, which can then be granted to different organizations or users directly, containing different applications. +--- + +# zitadel_project (Resource) + +Resource representing the project, which can then be granted to different organizations or users directly, containing different applications. + +## Example Usage + +```terraform +resource "zitadel_project" "default" { + name = "projectname" + org_id = data.zitadel_org.default.id + project_role_assertion = true + project_role_check = true + has_project_check = true + private_labeling_setting = "PRIVATE_LABELING_SETTING_ENFORCE_PROJECT_RESOURCE_OWNER_POLICY" +} +``` + + +## Schema + +### Required + +- `name` (String) Name of the project + +### Optional + +- `has_project_check` (Boolean) ZITADEL checks if the org of the user has permission to this project +- `org_id` (String) ID of the organization +- `private_labeling_setting` (String) Defines from where the private labeling should be triggered, supported values: PRIVATE_LABELING_SETTING_UNSPECIFIED, PRIVATE_LABELING_SETTING_ENFORCE_PROJECT_RESOURCE_OWNER_POLICY, PRIVATE_LABELING_SETTING_ALLOW_LOGIN_USER_RESOURCE_OWNER_POLICY +- `project_role_assertion` (Boolean) describes if roles of user should be added in token +- `project_role_check` (Boolean) ZITADEL checks if the user has at least one on this project + +### Read-Only + +- `id` (String) The ID of this resource. +- `state` (String) State of the project + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import project.imported '123456789012345678:123456789012345678' +``` diff --git a/docs/resources/project_grant.md b/docs/resources/project_grant.md new file mode 100644 index 00000000..8a0aa354 --- /dev/null +++ b/docs/resources/project_grant.md @@ -0,0 +1,45 @@ +--- +page_title: "zitadel_project_grant Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the grant of a project to a different organization, also containing the available roles which can be given to the members of the projectgrant. +--- + +# zitadel_project_grant (Resource) + +Resource representing the grant of a project to a different organization, also containing the available roles which can be given to the members of the projectgrant. + +## Example Usage + +```terraform +resource "zitadel_project_grant" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + granted_org_id = data.zitadel_org.granted_org.id + role_keys = ["super-user"] +} +``` + + +## Schema + +### Required + +- `granted_org_id` (String) ID of the organization granted the project +- `project_id` (String) ID of the project + +### Optional + +- `org_id` (String) ID of the organization +- `role_keys` (Set of String) List of roles granted + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import project_grant.imported '123456789012345678:123456789012345678:123456789012345678' +``` diff --git a/docs/resources/project_grant_member.md b/docs/resources/project_grant_member.md new file mode 100644 index 00000000..7b94e379 --- /dev/null +++ b/docs/resources/project_grant_member.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_project_grant_member Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the membership of a user on an granted project, defined with the given role. +--- + +# zitadel_project_grant_member (Resource) + +Resource representing the membership of a user on an granted project, defined with the given role. + +## Example Usage + +```terraform +resource "zitadel_project_grant_member" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + user_id = data.zitadel_human_user.default.id + grant_id = "123456789012345678" + roles = ["PROJECT_GRANT_OWNER"] +} +``` + + +## Schema + +### Required + +- `grant_id` (String) ID of the grant +- `project_id` (String) ID of the project +- `roles` (Set of String) List of roles granted +- `user_id` (String) ID of the user + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import project_grant_member.imported '123456789012345678:123456789012345678:123456789012345678:123456789012345678' +``` diff --git a/docs/resources/project_member.md b/docs/resources/project_member.md new file mode 100644 index 00000000..dd188b2a --- /dev/null +++ b/docs/resources/project_member.md @@ -0,0 +1,45 @@ +--- +page_title: "zitadel_project_member Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the membership of a user on an project, defined with the given role. +--- + +# zitadel_project_member (Resource) + +Resource representing the membership of a user on an project, defined with the given role. + +## Example Usage + +```terraform +resource "zitadel_project_member" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + user_id = data.zitadel_human_user.default.id + roles = ["PROJECT_OWNER"] +} +``` + + +## Schema + +### Required + +- `project_id` (String) ID of the project +- `roles` (Set of String) List of roles granted +- `user_id` (String) ID of the user + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import project_member.imported '123456789012345678:123456789012345678:123456789012345678' +``` diff --git a/docs/resources/project_role.md b/docs/resources/project_role.md new file mode 100644 index 00000000..63dcfeb2 --- /dev/null +++ b/docs/resources/project_role.md @@ -0,0 +1,47 @@ +--- +page_title: "zitadel_project_role Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the project roles, which can be given as authorizations to users. +--- + +# zitadel_project_role (Resource) + +Resource representing the project roles, which can be given as authorizations to users. + +## Example Usage + +```terraform +resource "zitadel_project_role" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + role_key = "super-user" + display_name = "display_name2" + group = "role_group" +} +``` + + +## Schema + +### Required + +- `display_name` (String) Name used for project role +- `project_id` (String) ID of the project +- `role_key` (String) Key used for project role + +### Optional + +- `group` (String) Group used for project role +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import project_role.imported '123456789012345678:my-role-key:123456789012345678' +``` diff --git a/docs/resources/sms_provider_twilio.md b/docs/resources/sms_provider_twilio.md new file mode 100644 index 00000000..02cc5973 --- /dev/null +++ b/docs/resources/sms_provider_twilio.md @@ -0,0 +1,40 @@ +--- +page_title: "zitadel_sms_provider_twilio Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the SMS provider Twilio configuration of an instance. +--- + +# zitadel_sms_provider_twilio (Resource) + +Resource representing the SMS provider Twilio configuration of an instance. + +## Example Usage + +```terraform +resource "zitadel_sms_provider_twilio" "default" { + sid = "sid" + sender_number = "019920892" + token = "twilio_token" +} +``` + + +## Schema + +### Required + +- `sender_number` (String) Sender number which is used to send the SMS. +- `sid` (String) SID used to communicate with Twilio. +- `token` (String, Sensitive) Token used to communicate with Twilio. + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import sms_provider_twilio.imported '123456789012345678:12345678901234567890123456abcdef' +``` diff --git a/docs/resources/smtp_config.md b/docs/resources/smtp_config.md new file mode 100644 index 00000000..8f779a17 --- /dev/null +++ b/docs/resources/smtp_config.md @@ -0,0 +1,49 @@ +--- +page_title: "zitadel_smtp_config Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the SMTP configuration of an instance. +--- + +# zitadel_smtp_config (Resource) + +Resource representing the SMTP configuration of an instance. + +## Example Usage + +```terraform +resource "zitadel_smtp_config" "default" { + sender_address = "sender@example.com" + sender_name = "no-reply" + tls = true + host = "localhost:25" + user = "user" + password = "secret_password" +} +``` + + +## Schema + +### Required + +- `host` (String) Host and port address to your SMTP server. +- `sender_address` (String) Address used to send emails. +- `sender_name` (String) Sender name used to send emails. + +### Optional + +- `password` (String, Sensitive) Password used to communicate with your SMTP server. +- `tls` (Boolean) TLS used to communicate with your SMTP server. +- `user` (String) User used to communicate with your SMTP server. + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format `<[password]>`, e.g. +terraform import smtp_config.imported 'p4ssw0rd' +``` diff --git a/docs/resources/trigger_actions.md b/docs/resources/trigger_actions.md new file mode 100644 index 00000000..ebcba7de --- /dev/null +++ b/docs/resources/trigger_actions.md @@ -0,0 +1,45 @@ +--- +page_title: "zitadel_trigger_actions Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing triggers, when actions get started +--- + +# zitadel_trigger_actions (Resource) + +Resource representing triggers, when actions get started + +## Example Usage + +```terraform +resource "zitadel_trigger_actions" "default" { + org_id = data.zitadel_org.default.id + flow_type = "FLOW_TYPE_CUSTOMISE_TOKEN" + trigger_type = "TRIGGER_TYPE_PRE_ACCESS_TOKEN_CREATION" + action_ids = [data.zitadel_action.default.id] +} +``` + + +## Schema + +### Required + +- `action_ids` (Set of String) IDs of the triggered actions +- `flow_type` (String) Type of the flow to which the action triggers belong, supported values: , FLOW_TYPE_EXTERNAL_AUTHENTICATION, FLOW_TYPE_CUSTOMISE_TOKEN +- `trigger_type` (String) Trigger type on when the actions get triggered, supported values: , TRIGGER_TYPE_POST_AUTHENTICATION, TRIGGER_TYPE_PRE_CREATION, TRIGGER_TYPE_POST_CREATION, TRIGGER_TYPE_PRE_USERINFO_CREATION + +### Optional + +- `org_id` (String) ID of the organization + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import trigger_actions.imported 'FLOW_TYPE_EXTERNAL_AUTHENTICATION:TRIGGER_TYPE_POST_CREATION:123456789012345678' +``` diff --git a/docs/resources/user_grant.md b/docs/resources/user_grant.md new file mode 100644 index 00000000..5dbcc579 --- /dev/null +++ b/docs/resources/user_grant.md @@ -0,0 +1,46 @@ +--- +page_title: "zitadel_user_grant Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + Resource representing the authorization given to a user directly, including the given roles. +--- + +# zitadel_user_grant (Resource) + +Resource representing the authorization given to a user directly, including the given roles. + +## Example Usage + +```terraform +resource "zitadel_user_grant" "default" { + project_id = data.zitadel_project.default.id + org_id = data.zitadel_org.default.id + role_keys = ["super-user"] + user_id = data.zitadel_human_user.default.id +} +``` + + +## Schema + +### Required + +- `user_id` (String) ID of the user + +### Optional + +- `org_id` (String) ID of the organization +- `project_grant_id` (String) ID of the granted project +- `project_id` (String) ID of the project +- `role_keys` (Set of String) List of roles granted + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +```terraform +# The resource can be imported using the ID format ``, e.g. +terraform import user_grant.imported '123456789012345678:123456789012345678:123456789012345678' +``` diff --git a/docs/resources/verify_email_message_text.md b/docs/resources/verify_email_message_text.md new file mode 100644 index 00000000..e5339d62 --- /dev/null +++ b/docs/resources/verify_email_message_text.md @@ -0,0 +1,49 @@ +--- +page_title: "zitadel_verify_email_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_verify_email_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_verify_email_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) +- `org_id` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/docs/resources/verify_phone_message_text.md b/docs/resources/verify_phone_message_text.md new file mode 100644 index 00000000..f5a3224e --- /dev/null +++ b/docs/resources/verify_phone_message_text.md @@ -0,0 +1,49 @@ +--- +page_title: "zitadel_verify_phone_message_text Resource - terraform-provider-zitadel" +subcategory: "" +description: |- + +--- + +# zitadel_verify_phone_message_text (Resource) + + + +## Example Usage + +```terraform +resource "zitadel_verify_phone_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} +``` + + +## Schema + +### Required + +- `language` (String) +- `org_id` (String) + +### Optional + +- `button_text` (String) +- `footer_text` (String) +- `greeting` (String) +- `pre_header` (String) +- `subject` (String) +- `text` (String) +- `title` (String) + +### Read-Only + +- `id` (String) The ID of this resource. \ No newline at end of file diff --git a/examples/migration/main.tf b/examples/migration/main.tf new file mode 100644 index 00000000..8a0f963c --- /dev/null +++ b/examples/migration/main.tf @@ -0,0 +1,98 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "0.0.0" + } + } +} + +provider zitadel { + issuer = "http://localhost:8080/oauth/v2" + address = "localhost:8080" + project = "160549024225689888" + token = "/Users/benz/go/src/github.com/zitadel/terraform-provider-zitadel/local-token" +} + + +data zitadelV1Org zitadelV1Org { + provider = zitadel + issuer = "https://issuer.zitadel.dev" + address = "api.zitadel.dev:443" + project = "70669147545070419" + token = "/Users/benz/go/src/github.com/zitadel/terraform-provider-zitadel/zitadel-dev-token" +} + +output fetched_org_id { + value = data.zitadelV1Org.zitadelV1Org.org +} + +output fetched_org_name { + value = data.zitadelV1Org.zitadelV1Org.name +} + +resource org org { + provider = zitadel + old_id = data.zitadelV1Org.zitadelV1Org.org + name = data.zitadelV1Org.zitadelV1Org.name +} + +resource user userTest { + depends_on = [data.zitadelV1Org.zitadelV1Org, org.org] + provider = zitadel + + for_each = { + for idx, user in data.zitadelV1Org.zitadelV1Org.users : user.user_name => user + } + old_id = each.value.id + resource_owner = org.org.id + state = each.value.state + user_name = each.value.user_name + login_names = each.value.login_names + preferred_login_name = each.value.preferred_login_name + type = each.value.type + first_name = each.value.first_name + last_name = each.value.last_name + nick_name = each.value.nick_name + display_name = each.value.display_name + preferred_language = each.value.preferred_language + gender = each.value.gender + phone = each.value.phone + is_phone_verified = each.value.is_phone_verified + email = each.value.email + is_email_verified = each.value.is_email_verified + name = each.value.name + description = each.value.description +} + + +resource project projectTest { + depends_on = [data.zitadelV1Org.zitadelV1Org, org.org] + provider = zitadel + + for_each = { + for idx, project in data.zitadelV1Org.zitadelV1Org.projects : project.name => project + } + old_id = each.value.id + name = each.value.name + state = each.value.state + resource_owner = org.org.id + project_role_assertion = each.value.project_role_assertion + project_role_check = each.value.project_role_check + has_project_check = each.value.has_project_check + private_labeling_setting = each.value.private_labeling_setting +} + + +resource domain domainTest { + depends_on = [data.zitadelV1Org.zitadelV1Org, org.org] + provider = zitadel + + for_each = { + for idx, domain in data.zitadelV1Org.zitadelV1Org.domains : domain.name => domain + } + + name = each.value.name + org_id = org.org.id +} + diff --git a/examples/provider/data-sources/action.tf b/examples/provider/data-sources/action.tf new file mode 100644 index 00000000..e5d2e4cb --- /dev/null +++ b/examples/provider/data-sources/action.tf @@ -0,0 +1,8 @@ +data "zitadel_action" "default" { + org_id = data.zitadel_org.default.id + action_id = "123456789012345678" +} + +output "action" { + value = data.zitadel_action.default +} diff --git a/examples/provider/data-sources/application_api.tf b/examples/provider/data-sources/application_api.tf new file mode 100644 index 00000000..fc5f7d18 --- /dev/null +++ b/examples/provider/data-sources/application_api.tf @@ -0,0 +1,9 @@ +data "zitadel_application_api" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + app_id = "123456789012345678" +} + +output "application_api" { + value = data.zitadel_application_api.default +} diff --git a/examples/provider/data-sources/application_oidc.tf b/examples/provider/data-sources/application_oidc.tf new file mode 100644 index 00000000..3b821841 --- /dev/null +++ b/examples/provider/data-sources/application_oidc.tf @@ -0,0 +1,9 @@ +data "zitadel_application_oidc" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + app_id = "123456789012345678" +} + +output "application_oidc" { + value = data.zitadel_application_oidc.default +} diff --git a/examples/provider/data-sources/default_oidc_settings.tf b/examples/provider/data-sources/default_oidc_settings.tf new file mode 100644 index 00000000..efcd8c99 --- /dev/null +++ b/examples/provider/data-sources/default_oidc_settings.tf @@ -0,0 +1,5 @@ +data "zitadel_default_oidc_settings" "default" {} + +output "oidc_settings" { + value = data.zitadel_default_oidc_settings.default +} diff --git a/examples/provider/data-sources/human_user.tf b/examples/provider/data-sources/human_user.tf new file mode 100644 index 00000000..34c22e2c --- /dev/null +++ b/examples/provider/data-sources/human_user.tf @@ -0,0 +1,8 @@ +data "zitadel_human_user" "default" { + org_id = data.zitadel_org.default.id + user_id = "123456789012345678" +} + +output "human_user" { + value = data.zitadel_human_user.default +} diff --git a/examples/provider/data-sources/idp_azure_ad.tf b/examples/provider/data-sources/idp_azure_ad.tf new file mode 100644 index 00000000..8bb667ed --- /dev/null +++ b/examples/provider/data-sources/idp_azure_ad.tf @@ -0,0 +1,3 @@ +data "zitadel_idp_azure_ad" "default" { + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/idp_github.tf b/examples/provider/data-sources/idp_github.tf new file mode 100644 index 00000000..7d755045 --- /dev/null +++ b/examples/provider/data-sources/idp_github.tf @@ -0,0 +1,3 @@ +data "zitadel_idp_github" "default" { + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/idp_github_es.tf b/examples/provider/data-sources/idp_github_es.tf new file mode 100644 index 00000000..9f94c463 --- /dev/null +++ b/examples/provider/data-sources/idp_github_es.tf @@ -0,0 +1,3 @@ +data "zitadel_idp_github_es" "default" { + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/idp_gitlab.tf b/examples/provider/data-sources/idp_gitlab.tf new file mode 100644 index 00000000..8933c0c7 --- /dev/null +++ b/examples/provider/data-sources/idp_gitlab.tf @@ -0,0 +1,3 @@ +data "zitadel_idp_gitlab" "default" { + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/idp_gitlab_self_hosted.tf b/examples/provider/data-sources/idp_gitlab_self_hosted.tf new file mode 100644 index 00000000..df488d8e --- /dev/null +++ b/examples/provider/data-sources/idp_gitlab_self_hosted.tf @@ -0,0 +1,3 @@ +data "zitadel_idp_gitlab_self_hosted" "default" { + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/idp_google.tf b/examples/provider/data-sources/idp_google.tf new file mode 100644 index 00000000..70f84587 --- /dev/null +++ b/examples/provider/data-sources/idp_google.tf @@ -0,0 +1,3 @@ +data "zitadel_idp_google" "default" { + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/idp_ldap.tf b/examples/provider/data-sources/idp_ldap.tf new file mode 100644 index 00000000..2ae61a04 --- /dev/null +++ b/examples/provider/data-sources/idp_ldap.tf @@ -0,0 +1,3 @@ +data "zitadel_idp_ldap" "default" { + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/machine_user.tf b/examples/provider/data-sources/machine_user.tf new file mode 100644 index 00000000..c3b529f6 --- /dev/null +++ b/examples/provider/data-sources/machine_user.tf @@ -0,0 +1,8 @@ +data "zitadel_machine_user" "default" { + org_id = data.zitadel_org.default.id + user_id = "123456789012345678" +} + +output "machine_user" { + value = data.zitadel_machine_user.default +} diff --git a/examples/provider/data-sources/org.tf b/examples/provider/data-sources/org.tf new file mode 100644 index 00000000..d61f0ff9 --- /dev/null +++ b/examples/provider/data-sources/org.tf @@ -0,0 +1,7 @@ +data "zitadel_org" "default" { + id = "123456789012345678" +} + +output "org" { + value = data.zitadel_org.default +} diff --git a/examples/provider/data-sources/org_idp_azure_ad.tf b/examples/provider/data-sources/org_idp_azure_ad.tf new file mode 100644 index 00000000..591b8ed8 --- /dev/null +++ b/examples/provider/data-sources/org_idp_azure_ad.tf @@ -0,0 +1,4 @@ +data "zitadel_org_idp_azure_ad" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/org_idp_github.tf b/examples/provider/data-sources/org_idp_github.tf new file mode 100644 index 00000000..94c5f4a2 --- /dev/null +++ b/examples/provider/data-sources/org_idp_github.tf @@ -0,0 +1,4 @@ +data "zitadel_org_idp_github" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/org_idp_github_es.tf b/examples/provider/data-sources/org_idp_github_es.tf new file mode 100644 index 00000000..cb81e2f0 --- /dev/null +++ b/examples/provider/data-sources/org_idp_github_es.tf @@ -0,0 +1,4 @@ +data "zitadel_org_idp_github_es" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/org_idp_gitlab.tf b/examples/provider/data-sources/org_idp_gitlab.tf new file mode 100644 index 00000000..935e394e --- /dev/null +++ b/examples/provider/data-sources/org_idp_gitlab.tf @@ -0,0 +1,4 @@ +data "zitadel_org_idp_gitlab" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/org_idp_gitlab_self_hosted.tf b/examples/provider/data-sources/org_idp_gitlab_self_hosted.tf new file mode 100644 index 00000000..7a3a6031 --- /dev/null +++ b/examples/provider/data-sources/org_idp_gitlab_self_hosted.tf @@ -0,0 +1,4 @@ +data "zitadel_org_idp_gitlab_self_hosted" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/org_idp_google.tf b/examples/provider/data-sources/org_idp_google.tf new file mode 100644 index 00000000..79ae6653 --- /dev/null +++ b/examples/provider/data-sources/org_idp_google.tf @@ -0,0 +1,4 @@ +data "zitadel_org_idp_google" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/org_idp_ldap.tf b/examples/provider/data-sources/org_idp_ldap.tf new file mode 100644 index 00000000..0a550c93 --- /dev/null +++ b/examples/provider/data-sources/org_idp_ldap.tf @@ -0,0 +1,4 @@ +data "zitadel_org_idp_ldap" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} diff --git a/examples/provider/data-sources/org_jwt_idp.tf b/examples/provider/data-sources/org_jwt_idp.tf new file mode 100644 index 00000000..ffe5dd1c --- /dev/null +++ b/examples/provider/data-sources/org_jwt_idp.tf @@ -0,0 +1,8 @@ +data "zitadel_org_jwt_idp" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} + +output "org_idp_org_jwt_idp" { + value = data.zitadel_org_jwt_idp.default +} diff --git a/examples/provider/data-sources/org_oidc_idp.tf b/examples/provider/data-sources/org_oidc_idp.tf new file mode 100644 index 00000000..a833d710 --- /dev/null +++ b/examples/provider/data-sources/org_oidc_idp.tf @@ -0,0 +1,8 @@ +data "zitadel_org_oidc_idp" "default" { + org_id = data.zitadel_org.default.id + id = "123456789012345678" +} + +output "org_oidc_idp" { + value = data.zitadel_org_oidc_idp.default +} diff --git a/examples/provider/data-sources/orgs.tf b/examples/provider/data-sources/orgs.tf new file mode 100644 index 00000000..939cb042 --- /dev/null +++ b/examples/provider/data-sources/orgs.tf @@ -0,0 +1,18 @@ +data "zitadel_orgs" "default" { + name = "example-name" + name_method = "TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE" + domain = "example.com" + domain_method = "TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE" + state = "ORG_STATE_ACTIVE" +} + +data "zitadel_org" "default" { + for_each = toset(data.zitadel_orgs.default.ids) + id = each.value +} + +output "org_names" { + value = toset([ + for org in data.zitadel_org.default : org.name + ]) +} diff --git a/examples/provider/data-sources/project.tf b/examples/provider/data-sources/project.tf new file mode 100644 index 00000000..4650cd8a --- /dev/null +++ b/examples/provider/data-sources/project.tf @@ -0,0 +1,8 @@ +data "zitadel_project" "default" { + org_id = data.zitadel_org.default.id + project_id = "123456789012345678" +} + +output "project" { + value = data.zitadel_project.default +} diff --git a/examples/provider/data-sources/project_role.tf b/examples/provider/data-sources/project_role.tf new file mode 100644 index 00000000..6669b021 --- /dev/null +++ b/examples/provider/data-sources/project_role.tf @@ -0,0 +1,9 @@ +data "zitadel_project_role" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + role_key = "key" +} + +output "project_role" { + value = data.zitadel_project_role.default +} diff --git a/examples/provider/data-sources/trigger_actions.tf b/examples/provider/data-sources/trigger_actions.tf new file mode 100644 index 00000000..806a1333 --- /dev/null +++ b/examples/provider/data-sources/trigger_actions.tf @@ -0,0 +1,9 @@ +data "zitadel_trigger_actions" "default" { + org_id = data.zitadel_org.default.id + flow_type = "FLOW_TYPE_EXTERNAL_AUTHENTICATION" + trigger_type = "TRIGGER_TYPE_POST_AUTHENTICATION" +} + +output "trigger_actions" { + value = data.zitadel_trigger_actions.default +} diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf new file mode 100644 index 00000000..f04e9ae3 --- /dev/null +++ b/examples/provider/provider.tf @@ -0,0 +1,16 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "1.0.0-alpha.16" + } + } +} + +provider "zitadel" { + domain = "localhost" + insecure = "true" + port = "8080" + project = "170832731415117995" + jwt_profile_file = "local-token" +} diff --git a/examples/provider/resources/action-import.sh b/examples/provider/resources/action-import.sh new file mode 100644 index 00000000..449d8f4b --- /dev/null +++ b/examples/provider/resources/action-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import action.imported '123456789012345678:123456789012345678' diff --git a/examples/provider/resources/action.tf b/examples/provider/resources/action.tf new file mode 100644 index 00000000..15a11924 --- /dev/null +++ b/examples/provider/resources/action.tf @@ -0,0 +1,7 @@ +resource "zitadel_action" "default" { + org_id = data.zitadel_org.default.id + name = "actionname" + script = "testscript" + timeout = "10s" + allowed_to_fail = true +} diff --git a/examples/provider/resources/application_api-import.sh b/examples/provider/resources/application_api-import.sh new file mode 100644 index 00000000..01612e33 --- /dev/null +++ b/examples/provider/resources/application_api-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import application_api.imported '123456789012345678:123456789012345678:123456789012345678:123456789012345678@zitadel:JuaDFFeOak5DGE655KCYPSAclSkbMVEJXXuX1lEMBT14eLMSs0A0qhafKX5SA2Df' diff --git a/examples/provider/resources/application_api.tf b/examples/provider/resources/application_api.tf new file mode 100644 index 00000000..37a837c9 --- /dev/null +++ b/examples/provider/resources/application_api.tf @@ -0,0 +1,6 @@ +resource "zitadel_application_api" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + name = "applicationapi" + auth_method_type = "API_AUTH_METHOD_TYPE_BASIC" +} diff --git a/examples/provider/resources/application_key-import.sh b/examples/provider/resources/application_key-import.sh new file mode 100644 index 00000000..a7e1df1b --- /dev/null +++ b/examples/provider/resources/application_key-import.sh @@ -0,0 +1,3 @@ +# The resource can be imported using the ID format ``. +# You can use __SEMICOLON__ to escape :, e.g. +terraform import application_key.imported "123456789012345678:123456789012345678:123456789012345678:123456789012345678:$(cat ~/Downloads/123456789012345678.json | sed -e 's/:/__SEMICOLON__/g')" diff --git a/examples/provider/resources/application_key.tf b/examples/provider/resources/application_key.tf new file mode 100644 index 00000000..a00da4f8 --- /dev/null +++ b/examples/provider/resources/application_key.tf @@ -0,0 +1,7 @@ +resource "zitadel_application_key" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + app_id = data.zitadel_application_api.default.id + key_type = "KEY_TYPE_JSON" + expiration_date = "2519-04-01T08:45:00Z" +} diff --git a/examples/provider/resources/application_oidc-import.sh b/examples/provider/resources/application_oidc-import.sh new file mode 100644 index 00000000..d180f6ea --- /dev/null +++ b/examples/provider/resources/application_oidc-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import application_oidc.imported '123456789012345678:123456789012345678:123456789012345678:123456789012345678@zitadel:JuaDFFeOak5DGE655KCYPSAclSkbMVEJXXuX1lEMBT14eLMSs0A0qhafKX5SA2Df' diff --git a/examples/provider/resources/application_oidc.tf b/examples/provider/resources/application_oidc.tf new file mode 100644 index 00000000..462aa5cf --- /dev/null +++ b/examples/provider/resources/application_oidc.tf @@ -0,0 +1,20 @@ +resource "zitadel_application_oidc" "default" { + project_id = data.zitadel_project.default.id + org_id = data.zitadel_org.default.id + + name = "applicationoidc" + redirect_uris = ["https://localhost.com"] + response_types = ["OIDC_RESPONSE_TYPE_CODE"] + grant_types = ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE"] + post_logout_redirect_uris = ["https://localhost.com"] + app_type = "OIDC_APP_TYPE_WEB" + auth_method_type = "OIDC_AUTH_METHOD_TYPE_BASIC" + version = "OIDC_VERSION_1_0" + clock_skew = "0s" + dev_mode = true + access_token_type = "OIDC_TOKEN_TYPE_BEARER" + access_token_role_assertion = false + id_token_role_assertion = false + id_token_userinfo_assertion = false + additional_origins = [] +} diff --git a/examples/provider/resources/default_domain_claimed_message_text.tf b/examples/provider/resources/default_domain_claimed_message_text.tf new file mode 100644 index 00000000..c0032b97 --- /dev/null +++ b/examples/provider/resources/default_domain_claimed_message_text.tf @@ -0,0 +1,11 @@ +resource "zitadel_default_domain_claimed_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/default_domain_policy-import.sh b/examples/provider/resources/default_domain_policy-import.sh new file mode 100644 index 00000000..cccbd21e --- /dev/null +++ b/examples/provider/resources/default_domain_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_domain_policy.imported '' diff --git a/examples/provider/resources/default_domain_policy.tf b/examples/provider/resources/default_domain_policy.tf new file mode 100644 index 00000000..ac158007 --- /dev/null +++ b/examples/provider/resources/default_domain_policy.tf @@ -0,0 +1,5 @@ +resource "zitadel_default_domain_policy" "default" { + user_login_must_be_domain = false + validate_org_domains = true + smtp_sender_address_matches_instance_domain = true +} diff --git a/examples/provider/resources/default_init_message_text.tf b/examples/provider/resources/default_init_message_text.tf new file mode 100644 index 00000000..e9b9fc83 --- /dev/null +++ b/examples/provider/resources/default_init_message_text.tf @@ -0,0 +1,11 @@ +resource "zitadel_default_init_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/default_label_policy-import.sh b/examples/provider/resources/default_label_policy-import.sh new file mode 100644 index 00000000..db070952 --- /dev/null +++ b/examples/provider/resources/default_label_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_label_policy.imported '' diff --git a/examples/provider/resources/default_label_policy.tf b/examples/provider/resources/default_label_policy.tf new file mode 100644 index 00000000..73544b6d --- /dev/null +++ b/examples/provider/resources/default_label_policy.tf @@ -0,0 +1,23 @@ +resource "zitadel_default_label_policy" "default" { + primary_color = "#5469d4" + hide_login_name_suffix = true + warn_color = "#cd3d56" + background_color = "#fafafa" + font_color = "#000000" + primary_color_dark = "#a5b4fc" + background_color_dark = "#111827" + warn_color_dark = "#ff3b5b" + font_color_dark = "#ffffff" + disable_watermark = false + set_active = true + logo_hash = filemd5("/path/to/logo.jpg") + logo_path = "/path/to/logo.jpg" + logo_dark_hash = filemd5("/path/to/logo_dark.jpg") + logo_dark_path = "/path/to/logo_dark.jpg" + icon_hash = filemd5("/path/to/icon.jpg") + icon_path = "/path/to/icon.jpg" + icon_dark_hash = filemd5("/path/to/icon_dark.jpg") + icon_dark_path = "/path/to/icon_dark.jpg" + font_hash = filemd5("/path/to/font.tff") + font_path = "/path/to/font.tff" +} diff --git a/examples/provider/resources/default_lockout_policy-import.sh b/examples/provider/resources/default_lockout_policy-import.sh new file mode 100644 index 00000000..708ec487 --- /dev/null +++ b/examples/provider/resources/default_lockout_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_lockout_policy.imported '' diff --git a/examples/provider/resources/default_lockout_policy.tf b/examples/provider/resources/default_lockout_policy.tf new file mode 100644 index 00000000..e65c5b33 --- /dev/null +++ b/examples/provider/resources/default_lockout_policy.tf @@ -0,0 +1,3 @@ +resource "zitadel_default_lockout_policy" "default" { + max_password_attempts = "5" +} diff --git a/examples/provider/resources/default_login_policy-import.sh b/examples/provider/resources/default_login_policy-import.sh new file mode 100644 index 00000000..e252f948 --- /dev/null +++ b/examples/provider/resources/default_login_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_login_policy.imported '' diff --git a/examples/provider/resources/default_login_policy.tf b/examples/provider/resources/default_login_policy.tf new file mode 100644 index 00000000..c1222012 --- /dev/null +++ b/examples/provider/resources/default_login_policy.tf @@ -0,0 +1,22 @@ +resource "zitadel_default_login_policy" "default" { + user_login = true + allow_register = true + allow_external_idp = true + force_mfa = false + force_mfa_local_only = false + passwordless_type = "PASSWORDLESS_TYPE_ALLOWED" + hide_password_reset = "false" + password_check_lifetime = "240h0m0s" + external_login_check_lifetime = "240h0m0s" + multi_factor_check_lifetime = "24h0m0s" + mfa_init_skip_lifetime = "720h0m0s" + second_factor_check_lifetime = "24h0m0s" + ignore_unknown_usernames = true + default_redirect_uri = "localhost:8080" + second_factors = ["SECOND_FACTOR_TYPE_OTP", "SECOND_FACTOR_TYPE_U2F"] + multi_factors = ["MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION"] + idps = [data.zitadel_idp_google.default.id, data.zitadel_idp_azure_ad.default.id] + allow_domain_discovery = true + disable_login_with_email = true + disable_login_with_phone = true +} diff --git a/examples/provider/resources/default_login_texts.tf b/examples/provider/resources/default_login_texts.tf new file mode 100644 index 00000000..9cee3674 --- /dev/null +++ b/examples/provider/resources/default_login_texts.tf @@ -0,0 +1,291 @@ +resource "zitadel_default_login_texts" "default" { + language = "en" + + email_verification_done_text = { + cancel_button_text = "example" + description = "example" + login_button_text = "example" + next_button_text = "example" + title = "example" + } + email_verification_text = { + code_label = "example" + description = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + external_registration_user_overview_text = { + back_button_text = "example" + description = "example" + email_label = "example" + firstname_label = "example" + language_label = "example" + lastname_label = "example" + next_button_text = "example" + nickname_label = "example" + phone_label = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + external_user_not_found_text = { + auto_register_button_text = "example" + description = "example" + link_button_text = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + } + footer_text = { + help = "example" + privacy_policy = "example" + tos = "example" + } + init_mfa_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + init_mfa_otp_text = { + cancel_button_text = "example" + code_label = "example" + description = "example" + description_otp = "example" + next_button_text = "example" + secret_label = "example" + title = "example" + } + init_mfa_prompt_text = { + description = "example" + next_button_text = "example" + otp_option = "example" + skip_button_text = "example" + title = "example" + u2f_option = "example" + } + init_mfa_u2f_text = { + description = "example" + error_retry = "example" + not_supported = "example" + register_token_button_text = "example" + title = "example" + token_name_label = "example" + } + init_password_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + init_password_text = { + code_label = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + initialize_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + initialize_user_text = { + code_label = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + linking_user_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + login_text = { + description = "example" + description_linking_process = "example" + external_user_description = "example" + login_name_label = "example" + login_name_placeholder = "example" + next_button_text = "example" + register_button_text = "example" + title = "example" + title_linking_process = "example" + user_must_be_member_of_org = "example" + user_name_placeholder = "example" + } + logout_text = { + description = "example" + login_button_text = "example" + title = "example" + } + mfa_providers_text = { + choose_other = "example" + otp = "example" + u2f = "example" + } + password_change_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + password_change_text = { + cancel_button_text = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + old_password_label = "example" + title = "example" + } + password_reset_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + password_text = { + back_button_text = "example" + confirmation = "example" + description = "example" + has_lowercase = "example" + has_number = "example" + has_symbol = "example" + has_uppercase = "example" + min_length = "example" + next_button_text = "example" + password_label = "example" + reset_link_text = "example" + title = "example" + } + passwordless_prompt_text = { + description = "example" + description_init = "example" + next_button_text = "example" + passwordless_button_text = "example" + skip_button_text = "example" + title = "example" + } + passwordless_registration_done_text = { + cancel_button_text = "example" + description = "example" + description_close = "example" + next_button_text = "example" + title = "example" + } + passwordless_registration_text = { + description = "example" + error_retry = "example" + not_supported = "example" + register_token_button_text = "example" + title = "example" + token_name_label = "example" + } + passwordless_text = { + description = "example" + error_retry = "example" + login_with_pw_button_text = "example" + not_supported = "example" + title = "example" + validate_token_button_text = "example" + } + registration_option_text = { + description = "example" + external_login_description = "example" + title = "example" + user_name_button_text = "example" + } + registration_org_text = { + description = "example" + email_label = "example" + firstname_label = "example" + lastname_label = "example" + orgname_label = "example" + password_confirm_label = "example" + password_label = "example" + privacy_link_text = "example" + save_button_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + registration_user_text = { + back_button_text = "example" + description = "example" + description_org_register = "example" + email_label = "example" + firstname_label = "example" + gender_label = "example" + language_label = "example" + lastname_label = "example" + next_button_text = "example" + password_confirm_label = "example" + password_label = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + select_account_text = { + description = "example" + description_linking_process = "example" + other_user = "example" + session_state_active = "example" + session_state_inactive = "example" + title = "example" + title_linking_process = "example" + user_must_be_member_of_org = "example" + } + success_login_text = { + auto_redirect_description = "example" + next_button_text = "example" + redirected_description = "example" + title = "example" + } + username_change_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + username_change_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + username_label = "example" + } + verify_mfa_otp_text = { + code_label = "example" + description = "example" + next_button_text = "example" + title = "example" + } + verify_mfa_u2f_text = { + description = "example" + error_retry = "example" + not_supported = "example" + title = "example" + validate_token_text = "example" + } +} + diff --git a/examples/provider/resources/default_notification_policy-import.sh b/examples/provider/resources/default_notification_policy-import.sh new file mode 100644 index 00000000..626de74e --- /dev/null +++ b/examples/provider/resources/default_notification_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_notification_policy.imported '' diff --git a/examples/provider/resources/default_notification_policy.tf b/examples/provider/resources/default_notification_policy.tf new file mode 100644 index 00000000..9d1970de --- /dev/null +++ b/examples/provider/resources/default_notification_policy.tf @@ -0,0 +1,3 @@ +resource "zitadel_default_notification_policy" "default" { + password_change = false +} diff --git a/examples/provider/resources/default_oidc_settings-import.sh b/examples/provider/resources/default_oidc_settings-import.sh new file mode 100644 index 00000000..e69de29b diff --git a/examples/provider/resources/default_oidc_settings.tf b/examples/provider/resources/default_oidc_settings.tf new file mode 100644 index 00000000..8f17d20d --- /dev/null +++ b/examples/provider/resources/default_oidc_settings.tf @@ -0,0 +1,6 @@ +resource "zitadel_default_oidc_settings" "default" { + access_token_lifetime = "12h0m0s" + id_token_lifetime = "12h0m0s" + refresh_token_expiration = "720h0m0s" + refresh_token_idle_expiration = "2160h0m0s" +} diff --git a/examples/provider/resources/default_password_change_message_text.tf b/examples/provider/resources/default_password_change_message_text.tf new file mode 100644 index 00000000..dcbe3362 --- /dev/null +++ b/examples/provider/resources/default_password_change_message_text.tf @@ -0,0 +1,11 @@ +resource "zitadel_default_password_change_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/default_password_complexity_policy-import.sh b/examples/provider/resources/default_password_complexity_policy-import.sh new file mode 100644 index 00000000..62a524cf --- /dev/null +++ b/examples/provider/resources/default_password_complexity_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_password_complexity_policy.imported '' diff --git a/examples/provider/resources/default_password_complexity_policy.tf b/examples/provider/resources/default_password_complexity_policy.tf new file mode 100644 index 00000000..9ac21fcb --- /dev/null +++ b/examples/provider/resources/default_password_complexity_policy.tf @@ -0,0 +1,7 @@ +resource "zitadel_default_password_complexity_policy" "default" { + min_length = "8" + has_uppercase = true + has_lowercase = true + has_number = true + has_symbol = true +} diff --git a/examples/provider/resources/default_password_reset_message_text.tf b/examples/provider/resources/default_password_reset_message_text.tf new file mode 100644 index 00000000..63127b1e --- /dev/null +++ b/examples/provider/resources/default_password_reset_message_text.tf @@ -0,0 +1,11 @@ +resource "zitadel_default_password_reset_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/default_passwordless_registration_message_text.tf b/examples/provider/resources/default_passwordless_registration_message_text.tf new file mode 100644 index 00000000..7c01e91f --- /dev/null +++ b/examples/provider/resources/default_passwordless_registration_message_text.tf @@ -0,0 +1,11 @@ +resource "zitadel_default_passwordless_registration_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/default_privacy_policy-import.sh b/examples/provider/resources/default_privacy_policy-import.sh new file mode 100644 index 00000000..e9c3b5b1 --- /dev/null +++ b/examples/provider/resources/default_privacy_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<>`, e.g. +terraform import default_privacy_policy.imported '' diff --git a/examples/provider/resources/default_privacy_policy.tf b/examples/provider/resources/default_privacy_policy.tf new file mode 100644 index 00000000..3daacf21 --- /dev/null +++ b/examples/provider/resources/default_privacy_policy.tf @@ -0,0 +1,6 @@ +resource "zitadel_default_privacy_policy" "default" { + tos_link = "https://example.com/tos" + privacy_link = "https://example.com/privacy" + help_link = "https://example.com/help" + support_email = "support@example.com" +} diff --git a/examples/provider/resources/default_verify_email_message_text.tf b/examples/provider/resources/default_verify_email_message_text.tf new file mode 100644 index 00000000..d5b05e1e --- /dev/null +++ b/examples/provider/resources/default_verify_email_message_text.tf @@ -0,0 +1,11 @@ +resource "zitadel_default_verify_email_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/default_verify_phone_message_text.tf b/examples/provider/resources/default_verify_phone_message_text.tf new file mode 100644 index 00000000..d2de41d5 --- /dev/null +++ b/examples/provider/resources/default_verify_phone_message_text.tf @@ -0,0 +1,11 @@ +resource "zitadel_default_verify_phone_message_text" "default" { + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/domain-import.sh b/examples/provider/resources/domain-import.sh new file mode 100644 index 00000000..b37205a5 --- /dev/null +++ b/examples/provider/resources/domain-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `name[:org_id]`, e.g. +terraform import domain.imported 'example.com:123456789012345678' diff --git a/examples/provider/resources/domain.tf b/examples/provider/resources/domain.tf new file mode 100644 index 00000000..babc7dfb --- /dev/null +++ b/examples/provider/resources/domain.tf @@ -0,0 +1,5 @@ +resource "zitadel_domain" "default" { + org_id = data.zitadel_org.default.id + name = "zitadel.default.127.0.0.1.sslip.io" + is_primary = false +} diff --git a/examples/provider/resources/domain_claimed_message_text.tf b/examples/provider/resources/domain_claimed_message_text.tf new file mode 100644 index 00000000..8d6c2dfc --- /dev/null +++ b/examples/provider/resources/domain_claimed_message_text.tf @@ -0,0 +1,12 @@ +resource "zitadel_domain_claimed_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/domain_policy-import.sh b/examples/provider/resources/domain_policy-import.sh new file mode 100644 index 00000000..3c609c8a --- /dev/null +++ b/examples/provider/resources/domain_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import domain_policy.imported '123456789012345678' diff --git a/examples/provider/resources/domain_policy.tf b/examples/provider/resources/domain_policy.tf new file mode 100644 index 00000000..a75590ac --- /dev/null +++ b/examples/provider/resources/domain_policy.tf @@ -0,0 +1,6 @@ +resource "zitadel_domain_policy" "default" { + org_id = data.zitadel_org.default.id + user_login_must_be_domain = false + validate_org_domains = true + smtp_sender_address_matches_instance_domain = true +} diff --git a/examples/provider/resources/granted_human_user-import.sh b/examples/provider/resources/granted_human_user-import.sh new file mode 100644 index 00000000..e69de29b diff --git a/examples/provider/resources/granted_human_user.tf b/examples/provider/resources/granted_human_user.tf new file mode 100644 index 00000000..ed05633c --- /dev/null +++ b/examples/provider/resources/granted_human_user.tf @@ -0,0 +1,14 @@ +resource "zitadel_human_user" "default" { + org_id = data.zitadel_org.default.id + user_name = "human@localhost" + first_name = "firstname" + last_name = "lastname" + nick_name = "nickname" + display_name = "displayname" + preferred_language = "de" + gender = "GENDER_MALE" + phone = "+41799999999" + is_phone_verified = true + email = "test@zitadel.com" + is_email_verified = false +} diff --git a/examples/provider/resources/human_user-import.sh b/examples/provider/resources/human_user-import.sh new file mode 100644 index 00000000..33523dfd --- /dev/null +++ b/examples/provider/resources/human_user-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `id[:org_id][:initial_password]>`, e.g. +terraform import human_user.imported '123456789012345678:123456789012345678:Password1!' diff --git a/examples/provider/resources/human_user.tf b/examples/provider/resources/human_user.tf new file mode 100644 index 00000000..ee14509f --- /dev/null +++ b/examples/provider/resources/human_user.tf @@ -0,0 +1,15 @@ +resource "zitadel_human_user" "default" { + org_id = data.zitadel_org.default.id + user_name = "humanfull@localhost.com" + first_name = "firstname" + last_name = "lastname" + nick_name = "nickname" + display_name = "displayname" + preferred_language = "de" + gender = "GENDER_MALE" + phone = "+41799999999" + is_phone_verified = true + email = "test@zitadel.com" + is_email_verified = true + initial_password = "Password1!" +} diff --git a/examples/provider/resources/idp_azure_ad-import.sh b/examples/provider/resources/idp_azure_ad-import.sh new file mode 100644 index 00000000..1595d239 --- /dev/null +++ b/examples/provider/resources/idp_azure_ad-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import idp_azure_ad.imported '123456789012345678:12345678-1234-1234-1234-123456789012' diff --git a/examples/provider/resources/idp_azure_ad.tf b/examples/provider/resources/idp_azure_ad.tf new file mode 100644 index 00000000..b6f87a57 --- /dev/null +++ b/examples/provider/resources/idp_azure_ad.tf @@ -0,0 +1,12 @@ +resource "zitadel_idp_azure_ad" "default" { + name = "Azure AD" + client_id = "9065bfc8-a08a..." + client_secret = "H2n***" + scopes = ["openid", "profile", "email", "User.Read"] + tenant_type = "AZURE_AD_TENANT_TYPE_ORGANISATIONS" + email_verified = true + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/idp_github-import.sh b/examples/provider/resources/idp_github-import.sh new file mode 100644 index 00000000..5e4e9b49 --- /dev/null +++ b/examples/provider/resources/idp_github-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import idp_github.imported '123456789012345678:1234567890123456781234567890123456787890' diff --git a/examples/provider/resources/idp_github.tf b/examples/provider/resources/idp_github.tf new file mode 100644 index 00000000..bbe729f9 --- /dev/null +++ b/examples/provider/resources/idp_github.tf @@ -0,0 +1,10 @@ +resource "zitadel_idp_github" "default" { + name = "GitHub" + client_id = "86a165..." + client_secret = "*****afdbac18" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/idp_github_es-import.sh b/examples/provider/resources/idp_github_es-import.sh new file mode 100644 index 00000000..a16b5d58 --- /dev/null +++ b/examples/provider/resources/idp_github_es-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import idp_github_es.imported '123456789012345678:1234567890123456781234567890123456787890' diff --git a/examples/provider/resources/idp_github_es.tf b/examples/provider/resources/idp_github_es.tf new file mode 100644 index 00000000..f7f985cf --- /dev/null +++ b/examples/provider/resources/idp_github_es.tf @@ -0,0 +1,13 @@ +resource "zitadel_idp_github_es" "default" { + name = "GitHub Enterprise Server" + client_id = "86a165..." + client_secret = "*****afdbac18" + scopes = ["openid", "profile", "email"] + authorization_endpoint = "https://auth.endpoint" + token_endpoint = "https://token.endpoint" + user_endpoint = "https://user.endpoint" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/idp_gitlab-import.sh b/examples/provider/resources/idp_gitlab-import.sh new file mode 100644 index 00000000..a5e451bc --- /dev/null +++ b/examples/provider/resources/idp_gitlab-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import idp_gitlab.imported '123456789012345678:1234567890abcdef' diff --git a/examples/provider/resources/idp_gitlab.tf b/examples/provider/resources/idp_gitlab.tf new file mode 100644 index 00000000..1440fe71 --- /dev/null +++ b/examples/provider/resources/idp_gitlab.tf @@ -0,0 +1,10 @@ +resource "zitadel_idp_gitlab" "default" { + name = "GitLab" + client_id = "15765e..." + client_secret = "*****abcxyz" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/idp_gitlab_self_hosted-import.sh b/examples/provider/resources/idp_gitlab_self_hosted-import.sh new file mode 100644 index 00000000..59939862 --- /dev/null +++ b/examples/provider/resources/idp_gitlab_self_hosted-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import idp_gitlab_self_hosted.imported '123456789012345678:1234567890abcdef' diff --git a/examples/provider/resources/idp_gitlab_self_hosted.tf b/examples/provider/resources/idp_gitlab_self_hosted.tf new file mode 100644 index 00000000..3914506c --- /dev/null +++ b/examples/provider/resources/idp_gitlab_self_hosted.tf @@ -0,0 +1,11 @@ +resource "zitadel_idp_gitlab_self_hosted" "default" { + name = "GitLab Self Hosted" + client_id = "15765e..." + client_secret = "*****abcxyz" + scopes = ["openid", "profile", "email"] + issuer = "https://my.issuer" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/idp_google-import.sh b/examples/provider/resources/idp_google-import.sh new file mode 100644 index 00000000..b942039e --- /dev/null +++ b/examples/provider/resources/idp_google-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import idp_google.imported '123456789012345678:G1234567890123' diff --git a/examples/provider/resources/idp_google.tf b/examples/provider/resources/idp_google.tf new file mode 100644 index 00000000..2f009cec --- /dev/null +++ b/examples/provider/resources/idp_google.tf @@ -0,0 +1,10 @@ +resource "zitadel_idp_google" "default" { + name = "Google" + client_id = "182902..." + client_secret = "GOCSPX-*****" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/idp_ldap-import.sh b/examples/provider/resources/idp_ldap-import.sh new file mode 100644 index 00000000..876d07b1 --- /dev/null +++ b/examples/provider/resources/idp_ldap-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import idp_ldap.imported '123456789012345678:b1nd_p4ssw0rd' diff --git a/examples/provider/resources/idp_ldap.tf b/examples/provider/resources/idp_ldap.tf new file mode 100644 index 00000000..ce3156f5 --- /dev/null +++ b/examples/provider/resources/idp_ldap.tf @@ -0,0 +1,21 @@ +resource "zitadel_idp_ldap" "default" { + name = "LDAP" + servers = ["ldaps://my.primary.server:389", "ldaps://my.secondary.server:389"] + start_tls = false + base_dn = "dc=example,dc=com" + bind_dn = "cn=admin,dc=example,dc=com" + bind_password = "Password1!" + user_base = "dn" + user_object_classes = ["inetOrgPerson"] + user_filters = ["uid", "email"] + timeout = "10s" + id_attribute = "uid" + first_name_attribute = "firstname" + last_name_attribute = "lastname" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} + + diff --git a/examples/provider/resources/init_message_text.tf b/examples/provider/resources/init_message_text.tf new file mode 100644 index 00000000..56d0ace4 --- /dev/null +++ b/examples/provider/resources/init_message_text.tf @@ -0,0 +1,12 @@ +resource "zitadel_init_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/instance_member-import.sh b/examples/provider/resources/instance_member-import.sh new file mode 100644 index 00000000..213c489f --- /dev/null +++ b/examples/provider/resources/instance_member-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import instance_member.imported '123456789012345678' diff --git a/examples/provider/resources/instance_member.tf b/examples/provider/resources/instance_member.tf new file mode 100644 index 00000000..feddb5dc --- /dev/null +++ b/examples/provider/resources/instance_member.tf @@ -0,0 +1,4 @@ +resource "zitadel_instance_member" "default" { + user_id = data.zitadel_human_user.default.id + roles = ["IAM_OWNER"] +} diff --git a/examples/provider/resources/label_policy-import.sh b/examples/provider/resources/label_policy-import.sh new file mode 100644 index 00000000..ecee24d2 --- /dev/null +++ b/examples/provider/resources/label_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import label_policy.imported '123456789012345678' diff --git a/examples/provider/resources/label_policy.tf b/examples/provider/resources/label_policy.tf new file mode 100644 index 00000000..345c754d --- /dev/null +++ b/examples/provider/resources/label_policy.tf @@ -0,0 +1,24 @@ +resource "zitadel_label_policy" "default" { + org_id = data.zitadel_org.default.id + primary_color = "#5469d4" + hide_login_name_suffix = true + warn_color = "#cd3d56" + background_color = "#fafafa" + font_color = "#000000" + primary_color_dark = "#a5b4fc" + background_color_dark = "#111827" + warn_color_dark = "#ff3b5b" + font_color_dark = "#ffffff" + disable_watermark = false + set_active = true + logo_hash = filemd5("/path/to/logo.jpg") + logo_path = "/path/to/logo.jpg" + logo_dark_hash = filemd5("/path/to/logo_dark.jpg") + logo_dark_path = "/path/to/logo_dark.jpg" + icon_hash = filemd5("/path/to/icon.jpg") + icon_path = "/path/to/icon.jpg" + icon_dark_hash = filemd5("/path/to/icon_dark.jpg") + icon_dark_path = "/path/to/icon_dark.jpg" + font_hash = filemd5("/path/to/font.tff") + font_path = "/path/to/font.tff" +} diff --git a/examples/provider/resources/lockout_policy-import.sh b/examples/provider/resources/lockout_policy-import.sh new file mode 100644 index 00000000..2f969857 --- /dev/null +++ b/examples/provider/resources/lockout_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import lockout_policy.imported '123456789012345678' diff --git a/examples/provider/resources/lockout_policy.tf b/examples/provider/resources/lockout_policy.tf new file mode 100644 index 00000000..3ae8b798 --- /dev/null +++ b/examples/provider/resources/lockout_policy.tf @@ -0,0 +1,4 @@ +resource "zitadel_lockout_policy" "default" { + org_id = data.zitadel_org.default.id + max_password_attempts = "5" +} diff --git a/examples/provider/resources/login_policy-import.sh b/examples/provider/resources/login_policy-import.sh new file mode 100644 index 00000000..c837b63b --- /dev/null +++ b/examples/provider/resources/login_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import login_policy.imported '123456789012345678' diff --git a/examples/provider/resources/login_policy.tf b/examples/provider/resources/login_policy.tf new file mode 100644 index 00000000..948a1c96 --- /dev/null +++ b/examples/provider/resources/login_policy.tf @@ -0,0 +1,23 @@ +resource "zitadel_login_policy" "default" { + org_id = data.zitadel_org.default.id + user_login = true + allow_register = true + allow_external_idp = true + force_mfa = false + force_mfa_local_only = false + passwordless_type = "PASSWORDLESS_TYPE_ALLOWED" + hide_password_reset = "false" + password_check_lifetime = "240h0m0s" + external_login_check_lifetime = "240h0m0s" + multi_factor_check_lifetime = "24h0m0s" + mfa_init_skip_lifetime = "720h0m0s" + second_factor_check_lifetime = "24h0m0s" + ignore_unknown_usernames = true + default_redirect_uri = "localhost:8080" + second_factors = ["SECOND_FACTOR_TYPE_OTP", "SECOND_FACTOR_TYPE_U2F"] + multi_factors = ["MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION"] + idps = [data.zitadel_idp_google.default.id, data.zitadel_idp_azure_ad.default.id] + allow_domain_discovery = true + disable_login_with_email = true + disable_login_with_phone = true +} diff --git a/examples/provider/resources/login_texts.tf b/examples/provider/resources/login_texts.tf new file mode 100644 index 00000000..2e4abecc --- /dev/null +++ b/examples/provider/resources/login_texts.tf @@ -0,0 +1,291 @@ +resource "zitadel_login_texts" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + email_verification_done_text = { + cancel_button_text = "example" + description = "example" + login_button_text = "example" + next_button_text = "example" + title = "example" + } + email_verification_text = { + code_label = "example" + description = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + external_registration_user_overview_text = { + back_button_text = "example" + description = "example" + email_label = "example" + firstname_label = "example" + language_label = "example" + lastname_label = "example" + next_button_text = "example" + nickname_label = "example" + phone_label = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + external_user_not_found_text = { + auto_register_button_text = "example" + description = "example" + link_button_text = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + } + footer_text = { + help = "example" + privacy_policy = "example" + tos = "example" + } + init_mfa_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + init_mfa_otp_text = { + cancel_button_text = "example" + code_label = "example" + description = "example" + description_otp = "example" + next_button_text = "example" + secret_label = "example" + title = "example" + } + init_mfa_prompt_text = { + description = "example" + next_button_text = "example" + otp_option = "example" + skip_button_text = "example" + title = "example" + u2f_option = "example" + } + init_mfa_u2f_text = { + description = "example" + error_retry = "example" + not_supported = "example" + register_token_button_text = "example" + title = "example" + token_name_label = "example" + } + init_password_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + init_password_text = { + code_label = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + initialize_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + initialize_user_text = { + code_label = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + resend_button_text = "example" + title = "example" + } + linking_user_done_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + } + login_text = { + description = "example" + description_linking_process = "example" + external_user_description = "example" + login_name_label = "example" + login_name_placeholder = "example" + next_button_text = "example" + register_button_text = "example" + title = "example" + title_linking_process = "example" + user_must_be_member_of_org = "example" + user_name_placeholder = "example" + } + logout_text = { + description = "example" + login_button_text = "example" + title = "example" + } + mfa_providers_text = { + choose_other = "example" + otp = "example" + u2f = "example" + } + password_change_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + password_change_text = { + cancel_button_text = "example" + description = "example" + new_password_confirm_label = "example" + new_password_label = "example" + next_button_text = "example" + old_password_label = "example" + title = "example" + } + password_reset_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + password_text = { + back_button_text = "example" + confirmation = "example" + description = "example" + has_lowercase = "example" + has_number = "example" + has_symbol = "example" + has_uppercase = "example" + min_length = "example" + next_button_text = "example" + password_label = "example" + reset_link_text = "example" + title = "example" + } + passwordless_prompt_text = { + description = "example" + description_init = "example" + next_button_text = "example" + passwordless_button_text = "example" + skip_button_text = "example" + title = "example" + } + passwordless_registration_done_text = { + cancel_button_text = "example" + description = "example" + description_close = "example" + next_button_text = "example" + title = "example" + } + passwordless_registration_text = { + description = "example" + error_retry = "example" + not_supported = "example" + register_token_button_text = "example" + title = "example" + token_name_label = "example" + } + passwordless_text = { + description = "example" + error_retry = "example" + login_with_pw_button_text = "example" + not_supported = "example" + title = "example" + validate_token_button_text = "example" + } + registration_option_text = { + description = "example" + external_login_description = "example" + title = "example" + user_name_button_text = "example" + } + registration_org_text = { + description = "example" + email_label = "example" + firstname_label = "example" + lastname_label = "example" + orgname_label = "example" + password_confirm_label = "example" + password_label = "example" + privacy_link_text = "example" + save_button_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + registration_user_text = { + back_button_text = "example" + description = "example" + description_org_register = "example" + email_label = "example" + firstname_label = "example" + gender_label = "example" + language_label = "example" + lastname_label = "example" + next_button_text = "example" + password_confirm_label = "example" + password_label = "example" + privacy_link_text = "example" + title = "example" + tos_and_privacy_label = "example" + tos_confirm = "example" + tos_confirm_and = "example" + tos_link_text = "example" + username_label = "example" + } + select_account_text = { + description = "example" + description_linking_process = "example" + other_user = "example" + session_state_active = "example" + session_state_inactive = "example" + title = "example" + title_linking_process = "example" + user_must_be_member_of_org = "example" + } + success_login_text = { + auto_redirect_description = "example" + next_button_text = "example" + redirected_description = "example" + title = "example" + } + username_change_done_text = { + description = "example" + next_button_text = "example" + title = "example" + } + username_change_text = { + cancel_button_text = "example" + description = "example" + next_button_text = "example" + title = "example" + username_label = "example" + } + verify_mfa_otp_text = { + code_label = "example" + description = "example" + next_button_text = "example" + title = "example" + } + verify_mfa_u2f_text = { + description = "example" + error_retry = "example" + not_supported = "example" + title = "example" + validate_token_text = "example" + } +} diff --git a/examples/provider/resources/machine_key-import.sh b/examples/provider/resources/machine_key-import.sh new file mode 100644 index 00000000..16c00e47 --- /dev/null +++ b/examples/provider/resources/machine_key-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import machine_key.imported '123456789012345678:123456789012345678:123456789012345678:{"type":"serviceaccount","keyId":"123456789012345678","key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpQ...-----END RSA PRIVATE KEY-----\n","userId":"123456789012345678"}' diff --git a/examples/provider/resources/machine_key.tf b/examples/provider/resources/machine_key.tf new file mode 100644 index 00000000..3a229b3e --- /dev/null +++ b/examples/provider/resources/machine_key.tf @@ -0,0 +1,6 @@ +resource "zitadel_machine_key" "default" { + org_id = data.zitadel_org.default.id + user_id = data.zitadel_machine_user.default.id + key_type = "KEY_TYPE_JSON" + expiration_date = "2519-04-01T08:45:00Z" +} diff --git a/examples/provider/resources/machine_user-import.sh b/examples/provider/resources/machine_user-import.sh new file mode 100644 index 00000000..3bcd0fbb --- /dev/null +++ b/examples/provider/resources/machine_user-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import machine_user.imported '123456789012345678:123456789012345678' diff --git a/examples/provider/resources/machine_user.tf b/examples/provider/resources/machine_user.tf new file mode 100644 index 00000000..358ec2e9 --- /dev/null +++ b/examples/provider/resources/machine_user.tf @@ -0,0 +1,6 @@ +resource "zitadel_machine_user" "default" { + org_id = data.zitadel_org.default.id + user_name = "machine@example.com" + name = "name" + description = "a machine user" +} diff --git a/examples/provider/resources/notification_policy-import.sh b/examples/provider/resources/notification_policy-import.sh new file mode 100644 index 00000000..f259e484 --- /dev/null +++ b/examples/provider/resources/notification_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import notification_policy.imported '123456789012345678' diff --git a/examples/provider/resources/notification_policy.tf b/examples/provider/resources/notification_policy.tf new file mode 100644 index 00000000..5bee21d9 --- /dev/null +++ b/examples/provider/resources/notification_policy.tf @@ -0,0 +1,4 @@ +resource "zitadel_notification_policy" "default" { + org_id = data.zitadel_org.default.id + password_change = false +} diff --git a/examples/provider/resources/org-import.sh b/examples/provider/resources/org-import.sh new file mode 100644 index 00000000..473304ee --- /dev/null +++ b/examples/provider/resources/org-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import org.imported '123456789012345678' diff --git a/examples/provider/resources/org.tf b/examples/provider/resources/org.tf new file mode 100644 index 00000000..78acd03c --- /dev/null +++ b/examples/provider/resources/org.tf @@ -0,0 +1,3 @@ +resource "zitadel_org" "default" { + name = "terraform-test" +} diff --git a/examples/provider/resources/org_idp_azure_ad-import.sh b/examples/provider/resources/org_idp_azure_ad-import.sh new file mode 100644 index 00000000..d472f5a3 --- /dev/null +++ b/examples/provider/resources/org_idp_azure_ad-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_azure_ad.imported '123456789012345678:123456789012345678:12345678-1234-1234-1234-123456789012' diff --git a/examples/provider/resources/org_idp_azure_ad.tf b/examples/provider/resources/org_idp_azure_ad.tf new file mode 100644 index 00000000..f6666dbc --- /dev/null +++ b/examples/provider/resources/org_idp_azure_ad.tf @@ -0,0 +1,13 @@ +resource "zitadel_org_idp_azure_ad" "default" { + org_id = data.zitadel_org.default.id + name = "Azure AD" + client_id = "9065bfc8-a08a..." + client_secret = "H2n***" + scopes = ["openid", "profile", "email", "User.Read"] + tenant_type = "AZURE_AD_TENANT_TYPE_ORGANISATIONS" + email_verified = true + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/org_idp_github-import.sh b/examples/provider/resources/org_idp_github-import.sh new file mode 100644 index 00000000..7cf99027 --- /dev/null +++ b/examples/provider/resources/org_idp_github-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_github.imported '123456789012345678:123456789012345678:1234567890123456781234567890123456787890' diff --git a/examples/provider/resources/org_idp_github.tf b/examples/provider/resources/org_idp_github.tf new file mode 100644 index 00000000..1261247f --- /dev/null +++ b/examples/provider/resources/org_idp_github.tf @@ -0,0 +1,11 @@ +resource "zitadel_org_idp_github" "default" { + org_id = data.zitadel_org.default.id + name = "GitHub" + client_id = "86a165..." + client_secret = "*****afdbac18" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/org_idp_github_es-import.sh b/examples/provider/resources/org_idp_github_es-import.sh new file mode 100644 index 00000000..58ea902f --- /dev/null +++ b/examples/provider/resources/org_idp_github_es-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_github_es.imported '123456789012345678:123456789012345678:123456789012345678:123456789012345678' diff --git a/examples/provider/resources/org_idp_github_es.tf b/examples/provider/resources/org_idp_github_es.tf new file mode 100644 index 00000000..2243681d --- /dev/null +++ b/examples/provider/resources/org_idp_github_es.tf @@ -0,0 +1,14 @@ +resource "zitadel_org_idp_github_es" "default" { + org_id = data.zitadel_org.default.id + name = "GitHub Enterprise Server" + client_id = "86a165..." + client_secret = "*****afdbac18" + scopes = ["openid", "profile", "email"] + authorization_endpoint = "https://auth.endpoint" + token_endpoint = "https://token.endpoint" + user_endpoint = "https://user.endpoint" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/org_idp_gitlab-import.sh b/examples/provider/resources/org_idp_gitlab-import.sh new file mode 100644 index 00000000..a161d990 --- /dev/null +++ b/examples/provider/resources/org_idp_gitlab-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_gitlab.imported '123456789012345678:123456789012345678:1234567890abcdef' diff --git a/examples/provider/resources/org_idp_gitlab.tf b/examples/provider/resources/org_idp_gitlab.tf new file mode 100644 index 00000000..4f4dcfa8 --- /dev/null +++ b/examples/provider/resources/org_idp_gitlab.tf @@ -0,0 +1,11 @@ +resource "zitadel_org_idp_gitlab" "default" { + org_id = data.zitadel_org.default.id + name = "GitLab" + client_id = "15765e..." + client_secret = "*****abcxyz" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/org_idp_gitlab_self_hosted-import.sh b/examples/provider/resources/org_idp_gitlab_self_hosted-import.sh new file mode 100644 index 00000000..e074469b --- /dev/null +++ b/examples/provider/resources/org_idp_gitlab_self_hosted-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_gitlab_self_hosted.imported '123456789012345678:123456789012345678:1234567890abcdef' diff --git a/examples/provider/resources/org_idp_gitlab_self_hosted.tf b/examples/provider/resources/org_idp_gitlab_self_hosted.tf new file mode 100644 index 00000000..fc64307e --- /dev/null +++ b/examples/provider/resources/org_idp_gitlab_self_hosted.tf @@ -0,0 +1,12 @@ +resource "zitadel_org_idp_gitlab_self_hosted" "default" { + org_id = data.zitadel_org.default.id + name = "GitLab Self Hosted" + client_id = "15765e..." + client_secret = "*****abcxyz" + scopes = ["openid", "profile", "email"] + issuer = "https://my.issuer" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/org_idp_google-import.sh b/examples/provider/resources/org_idp_google-import.sh new file mode 100644 index 00000000..df325207 --- /dev/null +++ b/examples/provider/resources/org_idp_google-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_google.imported '123456789012345678:123456789012345678:G1234567890123' diff --git a/examples/provider/resources/org_idp_google.tf b/examples/provider/resources/org_idp_google.tf new file mode 100644 index 00000000..1d01eec1 --- /dev/null +++ b/examples/provider/resources/org_idp_google.tf @@ -0,0 +1,11 @@ +resource "zitadel_org_idp_google" "default" { + org_id = data.zitadel_org.default.id + name = "Google" + client_id = "182902..." + client_secret = "GOCSPX-*****" + scopes = ["openid", "profile", "email"] + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} diff --git a/examples/provider/resources/org_idp_jwt-import.sh b/examples/provider/resources/org_idp_jwt-import.sh new file mode 100644 index 00000000..dd7b9c38 --- /dev/null +++ b/examples/provider/resources/org_idp_jwt-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_jwt.imported '123456789012345678:123456789012345678' diff --git a/examples/provider/resources/org_idp_jwt.tf b/examples/provider/resources/org_idp_jwt.tf new file mode 100644 index 00000000..39de56ed --- /dev/null +++ b/examples/provider/resources/org_idp_jwt.tf @@ -0,0 +1,10 @@ +resource "zitadel_org_idp_jwt" "default" { + org_id = data.zitadel_org.default.id + name = "jwtidp" + styling_type = "STYLING_TYPE_UNSPECIFIED" + jwt_endpoint = "https://jwtendpoint.com/jwt" + issuer = "https://google.com" + keys_endpoint = "https://jwtendpoint.com/keys" + header_name = "x-auth-token" + auto_register = false +} diff --git a/examples/provider/resources/org_idp_ldap-import.sh b/examples/provider/resources/org_idp_ldap-import.sh new file mode 100644 index 00000000..98edd18e --- /dev/null +++ b/examples/provider/resources/org_idp_ldap-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_ldap.imported '123456789012345678:123456789012345678:b1nd_p4ssw0rd' diff --git a/examples/provider/resources/org_idp_ldap.tf b/examples/provider/resources/org_idp_ldap.tf new file mode 100644 index 00000000..b9bc16c9 --- /dev/null +++ b/examples/provider/resources/org_idp_ldap.tf @@ -0,0 +1,22 @@ +resource "zitadel_org_idp_ldap" "default" { + org_id = data.zitadel_org.default.id + name = "LDAP" + servers = ["ldaps://my.primary.server:389", "ldaps://my.secondary.server:389"] + start_tls = false + base_dn = "dc=example,dc=com" + bind_dn = "cn=admin,dc=example,dc=com" + bind_password = "Password1!" + user_base = "dn" + user_object_classes = ["inetOrgPerson"] + user_filters = ["uid", "email"] + timeout = "10s" + id_attribute = "uid" + first_name_attribute = "firstname" + last_name_attribute = "lastname" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true +} + + diff --git a/examples/provider/resources/org_idp_oidc-import.sh b/examples/provider/resources/org_idp_oidc-import.sh new file mode 100644 index 00000000..6cd301de --- /dev/null +++ b/examples/provider/resources/org_idp_oidc-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import org_idp_oidc.imported '123456789012345678:123456789012345678:1234567890abcdef' diff --git a/examples/provider/resources/org_idp_oidc.tf b/examples/provider/resources/org_idp_oidc.tf new file mode 100644 index 00000000..ebdb19af --- /dev/null +++ b/examples/provider/resources/org_idp_oidc.tf @@ -0,0 +1,13 @@ +resource "zitadel_org_idp_oidc" "default" { + org_id = data.zitadel_org.default.id + name = "My Generic OIDC IDP" + client_id = "a_client_id" + client_secret = "a_client_secret" + scopes = ["openid", "profile", "email"] + issuer = "https://example.com" + is_linking_allowed = false + is_creation_allowed = true + is_auto_creation = false + is_auto_update = true + is_id_token_mapping = true +} diff --git a/examples/provider/resources/org_member-import.sh b/examples/provider/resources/org_member-import.sh new file mode 100644 index 00000000..527d4145 --- /dev/null +++ b/examples/provider/resources/org_member-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import org_member.imported '123456789012345678:123456789012345678' diff --git a/examples/provider/resources/org_member.tf b/examples/provider/resources/org_member.tf new file mode 100644 index 00000000..a182fba0 --- /dev/null +++ b/examples/provider/resources/org_member.tf @@ -0,0 +1,5 @@ +resource "zitadel_org_member" "default" { + org_id = data.zitadel_org.default.id + user_id = data.zitadel_human_user.default.id + roles = ["ORG_OWNER"] +} diff --git a/examples/provider/resources/password_change_message_text.tf b/examples/provider/resources/password_change_message_text.tf new file mode 100644 index 00000000..df5dff82 --- /dev/null +++ b/examples/provider/resources/password_change_message_text.tf @@ -0,0 +1,12 @@ +resource "zitadel_password_change_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/password_complexity_policy-import.sh b/examples/provider/resources/password_complexity_policy-import.sh new file mode 100644 index 00000000..e69de29b diff --git a/examples/provider/resources/password_complexity_policy.tf b/examples/provider/resources/password_complexity_policy.tf new file mode 100644 index 00000000..338421a0 --- /dev/null +++ b/examples/provider/resources/password_complexity_policy.tf @@ -0,0 +1,8 @@ +resource "zitadel_password_complexity_policy" "default" { + org_id = data.zitadel_org.default.id + min_length = "8" + has_uppercase = true + has_lowercase = true + has_number = true + has_symbol = true +} diff --git a/examples/provider/resources/password_reset_message_text.tf b/examples/provider/resources/password_reset_message_text.tf new file mode 100644 index 00000000..07fc2cc8 --- /dev/null +++ b/examples/provider/resources/password_reset_message_text.tf @@ -0,0 +1,12 @@ +resource "zitadel_password_reset_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/passwordless_registration_message_text.tf b/examples/provider/resources/passwordless_registration_message_text.tf new file mode 100644 index 00000000..ae83714b --- /dev/null +++ b/examples/provider/resources/passwordless_registration_message_text.tf @@ -0,0 +1,12 @@ +resource "zitadel_passwordless_registration_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/personal_access_token-import.sh b/examples/provider/resources/personal_access_token-import.sh new file mode 100644 index 00000000..6aaadc8d --- /dev/null +++ b/examples/provider/resources/personal_access_token-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import personal_access_token.imported '123456789012345678:123456789012345678:123456789012345678:LHt79...' diff --git a/examples/provider/resources/personal_access_token.tf b/examples/provider/resources/personal_access_token.tf new file mode 100644 index 00000000..9ad2cc65 --- /dev/null +++ b/examples/provider/resources/personal_access_token.tf @@ -0,0 +1,5 @@ +resource "zitadel_personal_access_token" "default" { + org_id = data.zitadel_org.default.id + user_id = data.zitadel_machine_user.default.id + expiration_date = "2519-04-01T08:45:00Z" +} diff --git a/examples/provider/resources/privacy_policy-import.sh b/examples/provider/resources/privacy_policy-import.sh new file mode 100644 index 00000000..8d7b61a8 --- /dev/null +++ b/examples/provider/resources/privacy_policy-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<[org_id]>`, e.g. +terraform import privacy_policy.imported '123456789012345678' diff --git a/examples/provider/resources/privacy_policy.tf b/examples/provider/resources/privacy_policy.tf new file mode 100644 index 00000000..ebc62b54 --- /dev/null +++ b/examples/provider/resources/privacy_policy.tf @@ -0,0 +1,7 @@ +resource "zitadel_privacy_policy" "default" { + org_id = data.zitadel_org.default.id + tos_link = "https://example.com/tos" + privacy_link = "https://example.com/privacy" + help_link = "https://example.com/help" + support_email = "support@example.com" +} diff --git a/examples/provider/resources/project-import.sh b/examples/provider/resources/project-import.sh new file mode 100644 index 00000000..3d8378d9 --- /dev/null +++ b/examples/provider/resources/project-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import project.imported '123456789012345678:123456789012345678' diff --git a/examples/provider/resources/project.tf b/examples/provider/resources/project.tf new file mode 100644 index 00000000..b3a1eb46 --- /dev/null +++ b/examples/provider/resources/project.tf @@ -0,0 +1,8 @@ +resource "zitadel_project" "default" { + name = "projectname" + org_id = data.zitadel_org.default.id + project_role_assertion = true + project_role_check = true + has_project_check = true + private_labeling_setting = "PRIVATE_LABELING_SETTING_ENFORCE_PROJECT_RESOURCE_OWNER_POLICY" +} diff --git a/examples/provider/resources/project_grant-import.sh b/examples/provider/resources/project_grant-import.sh new file mode 100644 index 00000000..327ebbde --- /dev/null +++ b/examples/provider/resources/project_grant-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import project_grant.imported '123456789012345678:123456789012345678:123456789012345678' diff --git a/examples/provider/resources/project_grant.tf b/examples/provider/resources/project_grant.tf new file mode 100644 index 00000000..c146a2f2 --- /dev/null +++ b/examples/provider/resources/project_grant.tf @@ -0,0 +1,6 @@ +resource "zitadel_project_grant" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + granted_org_id = data.zitadel_org.granted_org.id + role_keys = ["super-user"] +} diff --git a/examples/provider/resources/project_grant_member-import.sh b/examples/provider/resources/project_grant_member-import.sh new file mode 100644 index 00000000..be27f94e --- /dev/null +++ b/examples/provider/resources/project_grant_member-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import project_grant_member.imported '123456789012345678:123456789012345678:123456789012345678:123456789012345678' diff --git a/examples/provider/resources/project_grant_member.tf b/examples/provider/resources/project_grant_member.tf new file mode 100644 index 00000000..e27f15f5 --- /dev/null +++ b/examples/provider/resources/project_grant_member.tf @@ -0,0 +1,7 @@ +resource "zitadel_project_grant_member" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + user_id = data.zitadel_human_user.default.id + grant_id = "123456789012345678" + roles = ["PROJECT_GRANT_OWNER"] +} diff --git a/examples/provider/resources/project_member-import.sh b/examples/provider/resources/project_member-import.sh new file mode 100644 index 00000000..e19fbbbb --- /dev/null +++ b/examples/provider/resources/project_member-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import project_member.imported '123456789012345678:123456789012345678:123456789012345678' diff --git a/examples/provider/resources/project_member.tf b/examples/provider/resources/project_member.tf new file mode 100644 index 00000000..61436cc6 --- /dev/null +++ b/examples/provider/resources/project_member.tf @@ -0,0 +1,6 @@ +resource "zitadel_project_member" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + user_id = data.zitadel_human_user.default.id + roles = ["PROJECT_OWNER"] +} diff --git a/examples/provider/resources/project_role-import.sh b/examples/provider/resources/project_role-import.sh new file mode 100644 index 00000000..0791692c --- /dev/null +++ b/examples/provider/resources/project_role-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import project_role.imported '123456789012345678:my-role-key:123456789012345678' diff --git a/examples/provider/resources/project_role.tf b/examples/provider/resources/project_role.tf new file mode 100644 index 00000000..6347929f --- /dev/null +++ b/examples/provider/resources/project_role.tf @@ -0,0 +1,7 @@ +resource "zitadel_project_role" "default" { + org_id = data.zitadel_org.default.id + project_id = data.zitadel_project.default.id + role_key = "super-user" + display_name = "display_name2" + group = "role_group" +} diff --git a/examples/provider/resources/sms_provider_twilio-import.sh b/examples/provider/resources/sms_provider_twilio-import.sh new file mode 100644 index 00000000..2ea6a6ba --- /dev/null +++ b/examples/provider/resources/sms_provider_twilio-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import sms_provider_twilio.imported '123456789012345678:12345678901234567890123456abcdef' diff --git a/examples/provider/resources/sms_provider_twilio.tf b/examples/provider/resources/sms_provider_twilio.tf new file mode 100644 index 00000000..4c64e0a4 --- /dev/null +++ b/examples/provider/resources/sms_provider_twilio.tf @@ -0,0 +1,5 @@ +resource "zitadel_sms_provider_twilio" "default" { + sid = "sid" + sender_number = "019920892" + token = "twilio_token" +} diff --git a/examples/provider/resources/smtp_config-import.sh b/examples/provider/resources/smtp_config-import.sh new file mode 100644 index 00000000..722b2128 --- /dev/null +++ b/examples/provider/resources/smtp_config-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format `<[password]>`, e.g. +terraform import smtp_config.imported 'p4ssw0rd' diff --git a/examples/provider/resources/smtp_config.tf b/examples/provider/resources/smtp_config.tf new file mode 100644 index 00000000..51cfe619 --- /dev/null +++ b/examples/provider/resources/smtp_config.tf @@ -0,0 +1,8 @@ +resource "zitadel_smtp_config" "default" { + sender_address = "sender@example.com" + sender_name = "no-reply" + tls = true + host = "localhost:25" + user = "user" + password = "secret_password" +} diff --git a/examples/provider/resources/trigger_actions-import.sh b/examples/provider/resources/trigger_actions-import.sh new file mode 100644 index 00000000..6ef7ec07 --- /dev/null +++ b/examples/provider/resources/trigger_actions-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import trigger_actions.imported 'FLOW_TYPE_EXTERNAL_AUTHENTICATION:TRIGGER_TYPE_POST_CREATION:123456789012345678' diff --git a/examples/provider/resources/trigger_actions.tf b/examples/provider/resources/trigger_actions.tf new file mode 100644 index 00000000..6f324ad3 --- /dev/null +++ b/examples/provider/resources/trigger_actions.tf @@ -0,0 +1,6 @@ +resource "zitadel_trigger_actions" "default" { + org_id = data.zitadel_org.default.id + flow_type = "FLOW_TYPE_CUSTOMISE_TOKEN" + trigger_type = "TRIGGER_TYPE_PRE_ACCESS_TOKEN_CREATION" + action_ids = [data.zitadel_action.default.id] +} diff --git a/examples/provider/resources/user_grant-import.sh b/examples/provider/resources/user_grant-import.sh new file mode 100644 index 00000000..38d28e05 --- /dev/null +++ b/examples/provider/resources/user_grant-import.sh @@ -0,0 +1,2 @@ +# The resource can be imported using the ID format ``, e.g. +terraform import user_grant.imported '123456789012345678:123456789012345678:123456789012345678' diff --git a/examples/provider/resources/user_grant.tf b/examples/provider/resources/user_grant.tf new file mode 100644 index 00000000..ffe2099c --- /dev/null +++ b/examples/provider/resources/user_grant.tf @@ -0,0 +1,6 @@ +resource "zitadel_user_grant" "default" { + project_id = data.zitadel_project.default.id + org_id = data.zitadel_org.default.id + role_keys = ["super-user"] + user_id = data.zitadel_human_user.default.id +} diff --git a/examples/provider/resources/verify_email_message_text.tf b/examples/provider/resources/verify_email_message_text.tf new file mode 100644 index 00000000..b85837a3 --- /dev/null +++ b/examples/provider/resources/verify_email_message_text.tf @@ -0,0 +1,12 @@ +resource "zitadel_verify_email_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/examples/provider/resources/verify_phone_message_text.tf b/examples/provider/resources/verify_phone_message_text.tf new file mode 100644 index 00000000..379d8ed4 --- /dev/null +++ b/examples/provider/resources/verify_phone_message_text.tf @@ -0,0 +1,12 @@ +resource "zitadel_verify_phone_message_text" "default" { + org_id = data.zitadel_org.default.id + language = "en" + + title = "title example" + pre_header = "pre_header example" + subject = "subject example" + greeting = "greeting example" + text = "text example" + button_text = "button_text example" + footer_text = "footer_text example" +} diff --git a/gen.sh b/gen.sh new file mode 100755 index 00000000..506392e0 --- /dev/null +++ b/gen.sh @@ -0,0 +1,14 @@ +protoc \ + -I$(pwd) \ + -I$(go env GOPATH)/src/github.com/gogo/protobuf \ + -I$(go env GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway \ + -I$(go env GOPATH)/src/github.com/envoyproxy/protoc-gen-validate \ + -I$(go env GOPATH)/src/github.com/zitadel/zitadel/proto \ + --plugin=$(go env GOBIN)/protoc-gen-terraform \ + --terraform_out=config=gen/config.yaml:gen \ + $(go env GOPATH)/src/github.com/zitadel/zitadel/proto/zitadel/text.proto + +sed -i 's#_ "github.com/zitadel/zitadel/pkg/grpc/object"##g' gen/github.com/zitadel/zitadel/pkg/grpc/text/text_terraform.go +sed -i 's#textpb "textpb"#textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text"#g' gen/github.com/zitadel/zitadel/pkg/grpc/text/text_terraform.go +sed -i 's/U2f/U2F/g' gen/github.com/zitadel/zitadel/pkg/grpc/text/text_terraform.go + diff --git a/gen/config.yaml b/gen/config.yaml new file mode 100644 index 00000000..7f5eea20 --- /dev/null +++ b/gen/config.yaml @@ -0,0 +1,39 @@ +--- +types: + - LoginCustomText + - MessageCustomText + +target_package_name: text +default_package_name: textpb + +use_state_for_unknown_by_default: true +sort: true + +exclude_fields: + - LoginCustomText.details + - LoginCustomText.is_default + - MessageCustomText.details + - MessageCustomText.is_default + +injected_fields: + LoginCustomText: + - name: id + type: github.com/hashicorp/terraform-plugin-framework/types.StringType + computed: true + - name: org_id + type: github.com/hashicorp/terraform-plugin-framework/types.StringType + required: true + - name: language + type: github.com/hashicorp/terraform-plugin-framework/types.StringType + required: true + MessageCustomText: + - name: id + type: github.com/hashicorp/terraform-plugin-framework/types.StringType + computed: true + - name: org_id + type: github.com/hashicorp/terraform-plugin-framework/types.StringType + required: true + - name: language + type: github.com/hashicorp/terraform-plugin-framework/types.StringType + required: true + diff --git a/gen/github.com/zitadel/zitadel/pkg/grpc/text/text_terraform.go b/gen/github.com/zitadel/zitadel/pkg/grpc/text/text_terraform.go new file mode 100644 index 00000000..2d2bcb7c --- /dev/null +++ b/gen/github.com/zitadel/zitadel/pkg/grpc/text/text_terraform.go @@ -0,0 +1,12078 @@ +/* +Copyright 2015-2022 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: zitadel/text.proto + +package text + +import ( + context "context" + fmt "fmt" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + math "math" + + _ "github.com/envoyproxy/protoc-gen-validate/validate" + proto "github.com/gogo/protobuf/proto" + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + github_com_hashicorp_terraform_plugin_framework_attr "github.com/hashicorp/terraform-plugin-framework/attr" + github_com_hashicorp_terraform_plugin_framework_diag "github.com/hashicorp/terraform-plugin-framework/diag" + github_com_hashicorp_terraform_plugin_framework_tfsdk "github.com/hashicorp/terraform-plugin-framework/tfsdk" + github_com_hashicorp_terraform_plugin_framework_types "github.com/hashicorp/terraform-plugin-framework/types" + github_com_hashicorp_terraform_plugin_go_tftypes "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// GenSchemaLoginCustomText returns tfsdk.Schema definition for LoginCustomText +func GenSchemaLoginCustomText(ctx context.Context) (github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema, github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics) { + return github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema{Attributes: map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "email_verification_done_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "cancel_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "login_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "email_verification_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "code_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "resend_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "external_registration_user_overview_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "back_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "email_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "firstname_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "language_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "lastname_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "nickname_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "phone_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "privacy_confirm": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "privacy_link_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_and_privacy_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_confirm": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_link_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "username_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "external_user_not_found_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "auto_register_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "link_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "privacy_confirm": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "privacy_link_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_and_privacy_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_confirm": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_link_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "footer_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "help": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "privacy_policy": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "id": { + Computed: true, + Optional: false, + Required: false, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "init_mfa_done_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "cancel_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "init_mfa_otp_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "cancel_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "code_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description_otp": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "secret_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "init_mfa_prompt_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "otp_option": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "skip_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "u2f_option": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "init_mfa_u2f_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "error_retry": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "not_supported": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "register_token_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "token_name_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "init_password_done_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "cancel_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "init_password_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "code_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "new_password_confirm_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "new_password_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "resend_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "initialize_done_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "cancel_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "initialize_user_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "code_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "new_password_confirm_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "new_password_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "resend_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "language": { + Computed: false, + Optional: false, + Required: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "linking_user_done_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "cancel_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "login_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description_linking_process": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "external_user_description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "login_name_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "login_name_placeholder": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "register_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title_linking_process": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "user_must_be_member_of_org": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "user_name_placeholder": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "logout_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "login_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "mfa_providers_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "choose_other": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "otp": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "u2f": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "org_id": { + Computed: false, + Optional: false, + Required: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "password_change_done_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "password_change_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "cancel_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "new_password_confirm_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "new_password_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "old_password_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "password_reset_done_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "password_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "back_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "confirmation": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "has_lowercase": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "has_number": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "has_symbol": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "has_uppercase": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "min_length": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "password_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "reset_link_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "passwordless_prompt_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description_init": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "passwordless_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "skip_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "passwordless_registration_done_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "cancel_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description_close": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "passwordless_registration_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "error_retry": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "not_supported": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "register_token_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "token_name_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "passwordless_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "error_retry": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "login_with_pw_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "not_supported": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "validate_token_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "registration_option_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "external_login_description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "login_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "user_name_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "registration_org_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "email_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "firstname_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "lastname_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "orgname_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "password_confirm_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "password_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "privacy_confirm": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "privacy_link_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "save_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_and_privacy_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_confirm": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_link_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "username_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "registration_user_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "back_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description_org_register": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "email_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "firstname_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "gender_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "language_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "lastname_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "password_confirm_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "password_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "privacy_confirm": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "privacy_link_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_and_privacy_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_confirm": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "tos_link_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "username_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "select_account_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description_linking_process": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "other_user": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "session_state_active": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "session_state_inactive": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title_linking_process": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "user_must_be_member_of_org": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "success_login_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "auto_redirect_description": { + Description: "Text to describe that auto-redirect should happen after successful login", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "redirected_description": { + Description: "Text to describe that the window can be closed after redirect", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "username_change_done_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "username_change_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "cancel_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "username_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "verify_mfa_otp_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "code_label": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "next_button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + "verify_mfa_u2f_text": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "error_retry": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "not_supported": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "validate_token_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "", + Optional: true, + }, + }}, nil +} + +// GenSchemaMessageCustomText returns tfsdk.Schema definition for MessageCustomText +func GenSchemaMessageCustomText(ctx context.Context) (github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema, github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics) { + return github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema{Attributes: map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "button_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "footer_text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "greeting": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "id": { + Computed: true, + Optional: false, + Required: false, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "language": { + Computed: false, + Optional: false, + Required: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "org_id": { + Computed: false, + Optional: false, + Required: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "pre_header": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "subject": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "text": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "title": { + Description: "", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }}, nil +} + +// CopyLoginCustomTextFromTerraform copies contents of the source Terraform object into a target struct +func CopyLoginCustomTextFromTerraform(_ context.Context, tf github_com_hashicorp_terraform_plugin_framework_types.Object, obj *textpb.LoginCustomText) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + { + a, ok := tf.Attrs["email_verification_done_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_done_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_done_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.EmailVerificationDoneText = nil + if !v.Null && !v.Unknown { + tf := v + obj.EmailVerificationDoneText = &textpb.EmailVerificationDoneScreenText{} + obj := obj.EmailVerificationDoneText + { + a, ok := tf.Attrs["cancel_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_done_text.cancel_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CancelButtonText = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_done_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["login_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_done_text.login_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_done_text.login_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LoginButtonText = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_done_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_done_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["email_verification_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.EmailVerificationText = nil + if !v.Null && !v.Unknown { + tf := v + obj.EmailVerificationText = &textpb.EmailVerificationScreenText{} + obj := obj.EmailVerificationText + { + a, ok := tf.Attrs["code_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_text.code_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_text.code_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CodeLabel = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["resend_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_text.resend_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_text.resend_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ResendButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.email_verification_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.email_verification_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["external_registration_user_overview_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.ExternalRegistrationUserOverviewText = nil + if !v.Null && !v.Unknown { + tf := v + obj.ExternalRegistrationUserOverviewText = &textpb.ExternalRegistrationUserOverviewScreenText{} + obj := obj.ExternalRegistrationUserOverviewText + { + a, ok := tf.Attrs["back_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.back_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.back_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.BackButtonText = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["email_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.email_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.email_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.EmailLabel = t + } + } + } + { + a, ok := tf.Attrs["firstname_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.firstname_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.firstname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.FirstnameLabel = t + } + } + } + { + a, ok := tf.Attrs["language_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.language_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.language_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LanguageLabel = t + } + } + } + { + a, ok := tf.Attrs["lastname_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.lastname_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.lastname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LastnameLabel = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["nickname_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.nickname_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.nickname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NicknameLabel = t + } + } + } + { + a, ok := tf.Attrs["phone_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.phone_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.phone_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PhoneLabel = t + } + } + } + { + a, ok := tf.Attrs["privacy_confirm"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.privacy_confirm"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.privacy_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PrivacyConfirm = t + } + } + } + { + a, ok := tf.Attrs["privacy_link_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.privacy_link_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.privacy_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PrivacyLinkText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["tos_and_privacy_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.tos_and_privacy_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.tos_and_privacy_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosAndPrivacyLabel = t + } + } + } + { + a, ok := tf.Attrs["tos_confirm"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.tos_confirm"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.tos_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosConfirm = t + } + } + } + { + a, ok := tf.Attrs["tos_link_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.tos_link_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.tos_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosLinkText = t + } + } + } + { + a, ok := tf.Attrs["username_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_registration_user_overview_text.username_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.username_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.UsernameLabel = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["external_user_not_found_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_user_not_found_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_user_not_found_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.ExternalUserNotFoundText = nil + if !v.Null && !v.Unknown { + tf := v + obj.ExternalUserNotFoundText = &textpb.ExternalUserNotFoundScreenText{} + obj := obj.ExternalUserNotFoundText + { + a, ok := tf.Attrs["auto_register_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_user_not_found_text.auto_register_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_user_not_found_text.auto_register_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.AutoRegisterButtonText = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_user_not_found_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_user_not_found_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["link_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_user_not_found_text.link_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_user_not_found_text.link_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LinkButtonText = t + } + } + } + { + a, ok := tf.Attrs["privacy_confirm"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_user_not_found_text.privacy_confirm"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_user_not_found_text.privacy_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PrivacyConfirm = t + } + } + } + { + a, ok := tf.Attrs["privacy_link_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_user_not_found_text.privacy_link_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_user_not_found_text.privacy_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PrivacyLinkText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_user_not_found_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_user_not_found_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["tos_and_privacy_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_user_not_found_text.tos_and_privacy_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_user_not_found_text.tos_and_privacy_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosAndPrivacyLabel = t + } + } + } + { + a, ok := tf.Attrs["tos_confirm"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_user_not_found_text.tos_confirm"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_user_not_found_text.tos_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosConfirm = t + } + } + } + { + a, ok := tf.Attrs["tos_link_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.external_user_not_found_text.tos_link_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.external_user_not_found_text.tos_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosLinkText = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["footer_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.footer_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.footer_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.FooterText = nil + if !v.Null && !v.Unknown { + tf := v + obj.FooterText = &textpb.FooterText{} + obj := obj.FooterText + { + a, ok := tf.Attrs["help"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.footer_text.help"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.footer_text.help", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Help = t + } + } + } + { + a, ok := tf.Attrs["privacy_policy"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.footer_text.privacy_policy"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.footer_text.privacy_policy", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PrivacyPolicy = t + } + } + } + { + a, ok := tf.Attrs["tos"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.footer_text.tos"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.footer_text.tos", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Tos = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["init_mfa_done_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_done_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_done_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.InitMfaDoneText = nil + if !v.Null && !v.Unknown { + tf := v + obj.InitMfaDoneText = &textpb.InitMFADoneScreenText{} + obj := obj.InitMfaDoneText + { + a, ok := tf.Attrs["cancel_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_done_text.cancel_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CancelButtonText = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_done_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_done_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_done_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["init_mfa_otp_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_otp_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_otp_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.InitMfaOtpText = nil + if !v.Null && !v.Unknown { + tf := v + obj.InitMfaOtpText = &textpb.InitMFAOTPScreenText{} + obj := obj.InitMfaOtpText + { + a, ok := tf.Attrs["cancel_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_otp_text.cancel_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CancelButtonText = t + } + } + } + { + a, ok := tf.Attrs["code_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_otp_text.code_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.code_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CodeLabel = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_otp_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["description_otp"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_otp_text.description_otp"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.description_otp", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.DescriptionOtp = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_otp_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["secret_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_otp_text.secret_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.secret_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.SecretLabel = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_otp_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["init_mfa_prompt_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_prompt_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.InitMfaPromptText = nil + if !v.Null && !v.Unknown { + tf := v + obj.InitMfaPromptText = &textpb.InitMFAPromptScreenText{} + obj := obj.InitMfaPromptText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_prompt_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_prompt_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["otp_option"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_prompt_text.otp_option"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.otp_option", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.OtpOption = t + } + } + } + { + a, ok := tf.Attrs["skip_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_prompt_text.skip_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.skip_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.SkipButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_prompt_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["u2f_option"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_prompt_text.u2f_option"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.u2f_option", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.U2FOption = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["init_mfa_u2f_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_u2f_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.InitMfaU2FText = nil + if !v.Null && !v.Unknown { + tf := v + obj.InitMfaU2FText = &textpb.InitMFAU2FScreenText{} + obj := obj.InitMfaU2FText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_u2f_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["error_retry"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_u2f_text.error_retry"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.error_retry", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ErrorRetry = t + } + } + } + { + a, ok := tf.Attrs["not_supported"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_u2f_text.not_supported"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.not_supported", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NotSupported = t + } + } + } + { + a, ok := tf.Attrs["register_token_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_u2f_text.register_token_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.register_token_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.RegisterTokenButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_u2f_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["token_name_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_mfa_u2f_text.token_name_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.token_name_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TokenNameLabel = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["init_password_done_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_done_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_done_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.InitPasswordDoneText = nil + if !v.Null && !v.Unknown { + tf := v + obj.InitPasswordDoneText = &textpb.InitPasswordDoneScreenText{} + obj := obj.InitPasswordDoneText + { + a, ok := tf.Attrs["cancel_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_done_text.cancel_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CancelButtonText = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_done_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_done_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_done_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["init_password_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.InitPasswordText = nil + if !v.Null && !v.Unknown { + tf := v + obj.InitPasswordText = &textpb.InitPasswordScreenText{} + obj := obj.InitPasswordText + { + a, ok := tf.Attrs["code_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_text.code_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_text.code_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CodeLabel = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["new_password_confirm_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_text.new_password_confirm_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_text.new_password_confirm_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NewPasswordConfirmLabel = t + } + } + } + { + a, ok := tf.Attrs["new_password_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_text.new_password_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_text.new_password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NewPasswordLabel = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["resend_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_text.resend_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_text.resend_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ResendButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.init_password_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.init_password_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["initialize_done_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_done_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_done_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.InitializeDoneText = nil + if !v.Null && !v.Unknown { + tf := v + obj.InitializeDoneText = &textpb.InitializeUserDoneScreenText{} + obj := obj.InitializeDoneText + { + a, ok := tf.Attrs["cancel_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_done_text.cancel_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CancelButtonText = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_done_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_done_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_done_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["initialize_user_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_user_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_user_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.InitializeUserText = nil + if !v.Null && !v.Unknown { + tf := v + obj.InitializeUserText = &textpb.InitializeUserScreenText{} + obj := obj.InitializeUserText + { + a, ok := tf.Attrs["code_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_user_text.code_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_user_text.code_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CodeLabel = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_user_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_user_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["new_password_confirm_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_user_text.new_password_confirm_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_user_text.new_password_confirm_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NewPasswordConfirmLabel = t + } + } + } + { + a, ok := tf.Attrs["new_password_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_user_text.new_password_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_user_text.new_password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NewPasswordLabel = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_user_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_user_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["resend_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_user_text.resend_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_user_text.resend_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ResendButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.initialize_user_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.initialize_user_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["linking_user_done_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.linking_user_done_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.linking_user_done_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.LinkingUserDoneText = nil + if !v.Null && !v.Unknown { + tf := v + obj.LinkingUserDoneText = &textpb.LinkingUserDoneScreenText{} + obj := obj.LinkingUserDoneText + { + a, ok := tf.Attrs["cancel_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.linking_user_done_text.cancel_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.linking_user_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CancelButtonText = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.linking_user_done_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.linking_user_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.linking_user_done_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.linking_user_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.linking_user_done_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.linking_user_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["login_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.LoginText = nil + if !v.Null && !v.Unknown { + tf := v + obj.LoginText = &textpb.LoginScreenText{} + obj := obj.LoginText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["description_linking_process"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text.description_linking_process"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text.description_linking_process", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.DescriptionLinkingProcess = t + } + } + } + { + a, ok := tf.Attrs["external_user_description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text.external_user_description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text.external_user_description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ExternalUserDescription = t + } + } + } + { + a, ok := tf.Attrs["login_name_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text.login_name_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text.login_name_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LoginNameLabel = t + } + } + } + { + a, ok := tf.Attrs["login_name_placeholder"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text.login_name_placeholder"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text.login_name_placeholder", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LoginNamePlaceholder = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["register_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text.register_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text.register_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.RegisterButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["title_linking_process"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text.title_linking_process"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text.title_linking_process", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TitleLinkingProcess = t + } + } + } + { + a, ok := tf.Attrs["user_must_be_member_of_org"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text.user_must_be_member_of_org"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text.user_must_be_member_of_org", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.UserMustBeMemberOfOrg = t + } + } + } + { + a, ok := tf.Attrs["user_name_placeholder"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.login_text.user_name_placeholder"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.login_text.user_name_placeholder", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.UserNamePlaceholder = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["logout_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.logout_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.logout_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.LogoutText = nil + if !v.Null && !v.Unknown { + tf := v + obj.LogoutText = &textpb.LogoutDoneScreenText{} + obj := obj.LogoutText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.logout_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.logout_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["login_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.logout_text.login_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.logout_text.login_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LoginButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.logout_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.logout_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["mfa_providers_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.mfa_providers_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.mfa_providers_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.MfaProvidersText = nil + if !v.Null && !v.Unknown { + tf := v + obj.MfaProvidersText = &textpb.MFAProvidersText{} + obj := obj.MfaProvidersText + { + a, ok := tf.Attrs["choose_other"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.mfa_providers_text.choose_other"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.mfa_providers_text.choose_other", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ChooseOther = t + } + } + } + { + a, ok := tf.Attrs["otp"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.mfa_providers_text.otp"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.mfa_providers_text.otp", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Otp = t + } + } + } + { + a, ok := tf.Attrs["u2f"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.mfa_providers_text.u2f"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.mfa_providers_text.u2f", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.U2F = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["password_change_done_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_done_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_done_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.PasswordChangeDoneText = nil + if !v.Null && !v.Unknown { + tf := v + obj.PasswordChangeDoneText = &textpb.PasswordChangeDoneScreenText{} + obj := obj.PasswordChangeDoneText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_done_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_done_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_done_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["password_change_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.PasswordChangeText = nil + if !v.Null && !v.Unknown { + tf := v + obj.PasswordChangeText = &textpb.PasswordChangeScreenText{} + obj := obj.PasswordChangeText + { + a, ok := tf.Attrs["cancel_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_text.cancel_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CancelButtonText = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["new_password_confirm_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_text.new_password_confirm_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_text.new_password_confirm_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NewPasswordConfirmLabel = t + } + } + } + { + a, ok := tf.Attrs["new_password_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_text.new_password_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_text.new_password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NewPasswordLabel = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["old_password_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_text.old_password_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_text.old_password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.OldPasswordLabel = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_change_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_change_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["password_reset_done_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_reset_done_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_reset_done_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.PasswordResetDoneText = nil + if !v.Null && !v.Unknown { + tf := v + obj.PasswordResetDoneText = &textpb.PasswordResetDoneScreenText{} + obj := obj.PasswordResetDoneText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_reset_done_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_reset_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_reset_done_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_reset_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_reset_done_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_reset_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["password_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.PasswordText = nil + if !v.Null && !v.Unknown { + tf := v + obj.PasswordText = &textpb.PasswordScreenText{} + obj := obj.PasswordText + { + a, ok := tf.Attrs["back_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.back_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.back_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.BackButtonText = t + } + } + } + { + a, ok := tf.Attrs["confirmation"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.confirmation"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.confirmation", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Confirmation = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["has_lowercase"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.has_lowercase"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.has_lowercase", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.HasLowercase = t + } + } + } + { + a, ok := tf.Attrs["has_number"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.has_number"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.has_number", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.HasNumber = t + } + } + } + { + a, ok := tf.Attrs["has_symbol"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.has_symbol"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.has_symbol", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.HasSymbol = t + } + } + } + { + a, ok := tf.Attrs["has_uppercase"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.has_uppercase"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.has_uppercase", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.HasUppercase = t + } + } + } + { + a, ok := tf.Attrs["min_length"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.min_length"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.min_length", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.MinLength = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["password_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.password_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PasswordLabel = t + } + } + } + { + a, ok := tf.Attrs["reset_link_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.reset_link_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.reset_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ResetLinkText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.password_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.password_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["passwordless_prompt_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_prompt_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_prompt_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.PasswordlessPromptText = nil + if !v.Null && !v.Unknown { + tf := v + obj.PasswordlessPromptText = &textpb.PasswordlessPromptScreenText{} + obj := obj.PasswordlessPromptText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_prompt_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["description_init"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_prompt_text.description_init"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.description_init", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.DescriptionInit = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_prompt_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["passwordless_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_prompt_text.passwordless_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.passwordless_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PasswordlessButtonText = t + } + } + } + { + a, ok := tf.Attrs["skip_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_prompt_text.skip_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.skip_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.SkipButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_prompt_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["passwordless_registration_done_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_done_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.PasswordlessRegistrationDoneText = nil + if !v.Null && !v.Unknown { + tf := v + obj.PasswordlessRegistrationDoneText = &textpb.PasswordlessRegistrationDoneScreenText{} + obj := obj.PasswordlessRegistrationDoneText + { + a, ok := tf.Attrs["cancel_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_done_text.cancel_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CancelButtonText = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_done_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["description_close"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_done_text.description_close"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text.description_close", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.DescriptionClose = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_done_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_done_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["passwordless_registration_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.PasswordlessRegistrationText = nil + if !v.Null && !v.Unknown { + tf := v + obj.PasswordlessRegistrationText = &textpb.PasswordlessRegistrationScreenText{} + obj := obj.PasswordlessRegistrationText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["error_retry"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_text.error_retry"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_text.error_retry", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ErrorRetry = t + } + } + } + { + a, ok := tf.Attrs["not_supported"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_text.not_supported"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_text.not_supported", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NotSupported = t + } + } + } + { + a, ok := tf.Attrs["register_token_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_text.register_token_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_text.register_token_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.RegisterTokenButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["token_name_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_registration_text.token_name_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_registration_text.token_name_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TokenNameLabel = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["passwordless_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.PasswordlessText = nil + if !v.Null && !v.Unknown { + tf := v + obj.PasswordlessText = &textpb.PasswordlessScreenText{} + obj := obj.PasswordlessText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["error_retry"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_text.error_retry"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_text.error_retry", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ErrorRetry = t + } + } + } + { + a, ok := tf.Attrs["login_with_pw_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_text.login_with_pw_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_text.login_with_pw_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LoginWithPwButtonText = t + } + } + } + { + a, ok := tf.Attrs["not_supported"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_text.not_supported"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_text.not_supported", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NotSupported = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["validate_token_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.passwordless_text.validate_token_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.passwordless_text.validate_token_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ValidateTokenButtonText = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["registration_option_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_option_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_option_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.RegistrationOptionText = nil + if !v.Null && !v.Unknown { + tf := v + obj.RegistrationOptionText = &textpb.RegistrationOptionScreenText{} + obj := obj.RegistrationOptionText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_option_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_option_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["external_login_description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_option_text.external_login_description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_option_text.external_login_description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ExternalLoginDescription = t + } + } + } + { + a, ok := tf.Attrs["login_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_option_text.login_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_option_text.login_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LoginButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_option_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_option_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["user_name_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_option_text.user_name_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_option_text.user_name_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.UserNameButtonText = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["registration_org_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.RegistrationOrgText = nil + if !v.Null && !v.Unknown { + tf := v + obj.RegistrationOrgText = &textpb.RegistrationOrgScreenText{} + obj := obj.RegistrationOrgText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["email_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.email_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.email_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.EmailLabel = t + } + } + } + { + a, ok := tf.Attrs["firstname_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.firstname_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.firstname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.FirstnameLabel = t + } + } + } + { + a, ok := tf.Attrs["lastname_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.lastname_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.lastname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LastnameLabel = t + } + } + } + { + a, ok := tf.Attrs["orgname_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.orgname_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.orgname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.OrgnameLabel = t + } + } + } + { + a, ok := tf.Attrs["password_confirm_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.password_confirm_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.password_confirm_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PasswordConfirmLabel = t + } + } + } + { + a, ok := tf.Attrs["password_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.password_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PasswordLabel = t + } + } + } + { + a, ok := tf.Attrs["privacy_confirm"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.privacy_confirm"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.privacy_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PrivacyConfirm = t + } + } + } + { + a, ok := tf.Attrs["privacy_link_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.privacy_link_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.privacy_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PrivacyLinkText = t + } + } + } + { + a, ok := tf.Attrs["save_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.save_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.save_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.SaveButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["tos_and_privacy_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.tos_and_privacy_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.tos_and_privacy_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosAndPrivacyLabel = t + } + } + } + { + a, ok := tf.Attrs["tos_confirm"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.tos_confirm"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.tos_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosConfirm = t + } + } + } + { + a, ok := tf.Attrs["tos_link_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.tos_link_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.tos_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosLinkText = t + } + } + } + { + a, ok := tf.Attrs["username_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_org_text.username_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_org_text.username_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.UsernameLabel = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["registration_user_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.RegistrationUserText = nil + if !v.Null && !v.Unknown { + tf := v + obj.RegistrationUserText = &textpb.RegistrationUserScreenText{} + obj := obj.RegistrationUserText + { + a, ok := tf.Attrs["back_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.back_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.back_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.BackButtonText = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["description_org_register"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.description_org_register"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.description_org_register", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.DescriptionOrgRegister = t + } + } + } + { + a, ok := tf.Attrs["email_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.email_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.email_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.EmailLabel = t + } + } + } + { + a, ok := tf.Attrs["firstname_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.firstname_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.firstname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.FirstnameLabel = t + } + } + } + { + a, ok := tf.Attrs["gender_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.gender_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.gender_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.GenderLabel = t + } + } + } + { + a, ok := tf.Attrs["language_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.language_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.language_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LanguageLabel = t + } + } + } + { + a, ok := tf.Attrs["lastname_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.lastname_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.lastname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.LastnameLabel = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["password_confirm_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.password_confirm_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.password_confirm_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PasswordConfirmLabel = t + } + } + } + { + a, ok := tf.Attrs["password_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.password_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PasswordLabel = t + } + } + } + { + a, ok := tf.Attrs["privacy_confirm"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.privacy_confirm"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.privacy_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PrivacyConfirm = t + } + } + } + { + a, ok := tf.Attrs["privacy_link_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.privacy_link_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.privacy_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PrivacyLinkText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["tos_and_privacy_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.tos_and_privacy_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.tos_and_privacy_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosAndPrivacyLabel = t + } + } + } + { + a, ok := tf.Attrs["tos_confirm"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.tos_confirm"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.tos_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosConfirm = t + } + } + } + { + a, ok := tf.Attrs["tos_link_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.tos_link_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.tos_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TosLinkText = t + } + } + } + { + a, ok := tf.Attrs["username_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.registration_user_text.username_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.registration_user_text.username_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.UsernameLabel = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["select_account_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.select_account_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.select_account_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.SelectAccountText = nil + if !v.Null && !v.Unknown { + tf := v + obj.SelectAccountText = &textpb.SelectAccountScreenText{} + obj := obj.SelectAccountText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.select_account_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.select_account_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["description_linking_process"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.select_account_text.description_linking_process"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.select_account_text.description_linking_process", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.DescriptionLinkingProcess = t + } + } + } + { + a, ok := tf.Attrs["other_user"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.select_account_text.other_user"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.select_account_text.other_user", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.OtherUser = t + } + } + } + { + a, ok := tf.Attrs["session_state_active"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.select_account_text.session_state_active"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.select_account_text.session_state_active", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.SessionStateActive = t + } + } + } + { + a, ok := tf.Attrs["session_state_inactive"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.select_account_text.session_state_inactive"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.select_account_text.session_state_inactive", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.SessionStateInactive = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.select_account_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.select_account_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["title_linking_process"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.select_account_text.title_linking_process"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.select_account_text.title_linking_process", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.TitleLinkingProcess = t + } + } + } + { + a, ok := tf.Attrs["user_must_be_member_of_org"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.select_account_text.user_must_be_member_of_org"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.select_account_text.user_must_be_member_of_org", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.UserMustBeMemberOfOrg = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["success_login_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.success_login_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.success_login_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.SuccessLoginText = nil + if !v.Null && !v.Unknown { + tf := v + obj.SuccessLoginText = &textpb.SuccessLoginScreenText{} + obj := obj.SuccessLoginText + { + a, ok := tf.Attrs["auto_redirect_description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.success_login_text.auto_redirect_description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.success_login_text.auto_redirect_description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.AutoRedirectDescription = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.success_login_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.success_login_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["redirected_description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.success_login_text.redirected_description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.success_login_text.redirected_description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.RedirectedDescription = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.success_login_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.success_login_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["username_change_done_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.username_change_done_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.username_change_done_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.UsernameChangeDoneText = nil + if !v.Null && !v.Unknown { + tf := v + obj.UsernameChangeDoneText = &textpb.UsernameChangeDoneScreenText{} + obj := obj.UsernameChangeDoneText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.username_change_done_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.username_change_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.username_change_done_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.username_change_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.username_change_done_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.username_change_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["username_change_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.username_change_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.username_change_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.UsernameChangeText = nil + if !v.Null && !v.Unknown { + tf := v + obj.UsernameChangeText = &textpb.UsernameChangeScreenText{} + obj := obj.UsernameChangeText + { + a, ok := tf.Attrs["cancel_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.username_change_text.cancel_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.username_change_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CancelButtonText = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.username_change_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.username_change_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.username_change_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.username_change_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.username_change_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.username_change_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["username_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.username_change_text.username_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.username_change_text.username_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.UsernameLabel = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["verify_mfa_otp_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.verify_mfa_otp_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.verify_mfa_otp_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.VerifyMfaOtpText = nil + if !v.Null && !v.Unknown { + tf := v + obj.VerifyMfaOtpText = &textpb.VerifyMFAOTPScreenText{} + obj := obj.VerifyMfaOtpText + { + a, ok := tf.Attrs["code_label"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.verify_mfa_otp_text.code_label"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.verify_mfa_otp_text.code_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.CodeLabel = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.verify_mfa_otp_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.verify_mfa_otp_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["next_button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.verify_mfa_otp_text.next_button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.verify_mfa_otp_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NextButtonText = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.verify_mfa_otp_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.verify_mfa_otp_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["verify_mfa_u2f_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.verify_mfa_u2f_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.VerifyMfaU2FText = nil + if !v.Null && !v.Unknown { + tf := v + obj.VerifyMfaU2FText = &textpb.VerifyMFAU2FScreenText{} + obj := obj.VerifyMfaU2FText + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.verify_mfa_u2f_text.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["error_retry"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.verify_mfa_u2f_text.error_retry"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text.error_retry", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ErrorRetry = t + } + } + } + { + a, ok := tf.Attrs["not_supported"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.verify_mfa_u2f_text.not_supported"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text.not_supported", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.NotSupported = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.verify_mfa_u2f_text.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + { + a, ok := tf.Attrs["validate_token_text"] + if !ok { + diags.Append(attrReadMissingDiag{"LoginCustomText.verify_mfa_u2f_text.validate_token_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text.validate_token_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ValidateTokenText = t + } + } + } + } + } + } + } + return diags +} + +// CopyLoginCustomTextToTerraform copies contents of the source Terraform object into a target struct +func CopyLoginCustomTextToTerraform(ctx context.Context, obj textpb.LoginCustomText, tf *github_com_hashicorp_terraform_plugin_framework_types.Object) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + tf.Null = false + tf.Unknown = false + if tf.Attrs == nil { + tf.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value) + } + { + a, ok := tf.AttrTypes["email_verification_done_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_done_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_done_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["email_verification_done_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.EmailVerificationDoneText == nil { + v.Null = true + } else { + obj := obj.EmailVerificationDoneText + tf := &v + { + t, ok := tf.AttrTypes["cancel_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_done_text.cancel_button_text"}) + } else { + v, ok := tf.Attrs["cancel_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.email_verification_done_text.cancel_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CancelButtonText) == "" + } + v.Value = string(obj.CancelButtonText) + v.Unknown = false + tf.Attrs["cancel_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_done_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.email_verification_done_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["login_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_done_text.login_button_text"}) + } else { + v, ok := tf.Attrs["login_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.email_verification_done_text.login_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_done_text.login_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LoginButtonText) == "" + } + v.Value = string(obj.LoginButtonText) + v.Unknown = false + tf.Attrs["login_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_done_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.email_verification_done_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_done_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.email_verification_done_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["email_verification_done_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["email_verification_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["email_verification_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.EmailVerificationText == nil { + v.Null = true + } else { + obj := obj.EmailVerificationText + tf := &v + { + t, ok := tf.AttrTypes["code_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_text.code_label"}) + } else { + v, ok := tf.Attrs["code_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.email_verification_text.code_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_text.code_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CodeLabel) == "" + } + v.Value = string(obj.CodeLabel) + v.Unknown = false + tf.Attrs["code_label"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.email_verification_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.email_verification_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["resend_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_text.resend_button_text"}) + } else { + v, ok := tf.Attrs["resend_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.email_verification_text.resend_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_text.resend_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ResendButtonText) == "" + } + v.Value = string(obj.ResendButtonText) + v.Unknown = false + tf.Attrs["resend_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.email_verification_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.email_verification_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.email_verification_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["email_verification_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["external_registration_user_overview_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["external_registration_user_overview_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.ExternalRegistrationUserOverviewText == nil { + v.Null = true + } else { + obj := obj.ExternalRegistrationUserOverviewText + tf := &v + { + t, ok := tf.AttrTypes["back_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.back_button_text"}) + } else { + v, ok := tf.Attrs["back_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.back_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.back_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.BackButtonText) == "" + } + v.Value = string(obj.BackButtonText) + v.Unknown = false + tf.Attrs["back_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["email_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.email_label"}) + } else { + v, ok := tf.Attrs["email_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.email_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.email_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.EmailLabel) == "" + } + v.Value = string(obj.EmailLabel) + v.Unknown = false + tf.Attrs["email_label"] = v + } + } + { + t, ok := tf.AttrTypes["firstname_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.firstname_label"}) + } else { + v, ok := tf.Attrs["firstname_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.firstname_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.firstname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.FirstnameLabel) == "" + } + v.Value = string(obj.FirstnameLabel) + v.Unknown = false + tf.Attrs["firstname_label"] = v + } + } + { + t, ok := tf.AttrTypes["language_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.language_label"}) + } else { + v, ok := tf.Attrs["language_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.language_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.language_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LanguageLabel) == "" + } + v.Value = string(obj.LanguageLabel) + v.Unknown = false + tf.Attrs["language_label"] = v + } + } + { + t, ok := tf.AttrTypes["lastname_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.lastname_label"}) + } else { + v, ok := tf.Attrs["lastname_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.lastname_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.lastname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LastnameLabel) == "" + } + v.Value = string(obj.LastnameLabel) + v.Unknown = false + tf.Attrs["lastname_label"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["nickname_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.nickname_label"}) + } else { + v, ok := tf.Attrs["nickname_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.nickname_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.nickname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NicknameLabel) == "" + } + v.Value = string(obj.NicknameLabel) + v.Unknown = false + tf.Attrs["nickname_label"] = v + } + } + { + t, ok := tf.AttrTypes["phone_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.phone_label"}) + } else { + v, ok := tf.Attrs["phone_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.phone_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.phone_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PhoneLabel) == "" + } + v.Value = string(obj.PhoneLabel) + v.Unknown = false + tf.Attrs["phone_label"] = v + } + } + { + t, ok := tf.AttrTypes["privacy_confirm"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.privacy_confirm"}) + } else { + v, ok := tf.Attrs["privacy_confirm"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.privacy_confirm", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.privacy_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PrivacyConfirm) == "" + } + v.Value = string(obj.PrivacyConfirm) + v.Unknown = false + tf.Attrs["privacy_confirm"] = v + } + } + { + t, ok := tf.AttrTypes["privacy_link_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.privacy_link_text"}) + } else { + v, ok := tf.Attrs["privacy_link_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.privacy_link_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.privacy_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PrivacyLinkText) == "" + } + v.Value = string(obj.PrivacyLinkText) + v.Unknown = false + tf.Attrs["privacy_link_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["tos_and_privacy_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.tos_and_privacy_label"}) + } else { + v, ok := tf.Attrs["tos_and_privacy_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.tos_and_privacy_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.tos_and_privacy_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosAndPrivacyLabel) == "" + } + v.Value = string(obj.TosAndPrivacyLabel) + v.Unknown = false + tf.Attrs["tos_and_privacy_label"] = v + } + } + { + t, ok := tf.AttrTypes["tos_confirm"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.tos_confirm"}) + } else { + v, ok := tf.Attrs["tos_confirm"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.tos_confirm", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.tos_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosConfirm) == "" + } + v.Value = string(obj.TosConfirm) + v.Unknown = false + tf.Attrs["tos_confirm"] = v + } + } + { + t, ok := tf.AttrTypes["tos_link_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.tos_link_text"}) + } else { + v, ok := tf.Attrs["tos_link_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.tos_link_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.tos_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosLinkText) == "" + } + v.Value = string(obj.TosLinkText) + v.Unknown = false + tf.Attrs["tos_link_text"] = v + } + } + { + t, ok := tf.AttrTypes["username_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_registration_user_overview_text.username_label"}) + } else { + v, ok := tf.Attrs["username_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_registration_user_overview_text.username_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_registration_user_overview_text.username_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.UsernameLabel) == "" + } + v.Value = string(obj.UsernameLabel) + v.Unknown = false + tf.Attrs["username_label"] = v + } + } + } + v.Unknown = false + tf.Attrs["external_registration_user_overview_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["external_user_not_found_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_user_not_found_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_user_not_found_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["external_user_not_found_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.ExternalUserNotFoundText == nil { + v.Null = true + } else { + obj := obj.ExternalUserNotFoundText + tf := &v + { + t, ok := tf.AttrTypes["auto_register_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_user_not_found_text.auto_register_button_text"}) + } else { + v, ok := tf.Attrs["auto_register_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_user_not_found_text.auto_register_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_user_not_found_text.auto_register_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.AutoRegisterButtonText) == "" + } + v.Value = string(obj.AutoRegisterButtonText) + v.Unknown = false + tf.Attrs["auto_register_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_user_not_found_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_user_not_found_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_user_not_found_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["link_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_user_not_found_text.link_button_text"}) + } else { + v, ok := tf.Attrs["link_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_user_not_found_text.link_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_user_not_found_text.link_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LinkButtonText) == "" + } + v.Value = string(obj.LinkButtonText) + v.Unknown = false + tf.Attrs["link_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["privacy_confirm"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_user_not_found_text.privacy_confirm"}) + } else { + v, ok := tf.Attrs["privacy_confirm"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_user_not_found_text.privacy_confirm", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_user_not_found_text.privacy_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PrivacyConfirm) == "" + } + v.Value = string(obj.PrivacyConfirm) + v.Unknown = false + tf.Attrs["privacy_confirm"] = v + } + } + { + t, ok := tf.AttrTypes["privacy_link_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_user_not_found_text.privacy_link_text"}) + } else { + v, ok := tf.Attrs["privacy_link_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_user_not_found_text.privacy_link_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_user_not_found_text.privacy_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PrivacyLinkText) == "" + } + v.Value = string(obj.PrivacyLinkText) + v.Unknown = false + tf.Attrs["privacy_link_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_user_not_found_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_user_not_found_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_user_not_found_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["tos_and_privacy_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_user_not_found_text.tos_and_privacy_label"}) + } else { + v, ok := tf.Attrs["tos_and_privacy_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_user_not_found_text.tos_and_privacy_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_user_not_found_text.tos_and_privacy_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosAndPrivacyLabel) == "" + } + v.Value = string(obj.TosAndPrivacyLabel) + v.Unknown = false + tf.Attrs["tos_and_privacy_label"] = v + } + } + { + t, ok := tf.AttrTypes["tos_confirm"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_user_not_found_text.tos_confirm"}) + } else { + v, ok := tf.Attrs["tos_confirm"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_user_not_found_text.tos_confirm", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_user_not_found_text.tos_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosConfirm) == "" + } + v.Value = string(obj.TosConfirm) + v.Unknown = false + tf.Attrs["tos_confirm"] = v + } + } + { + t, ok := tf.AttrTypes["tos_link_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.external_user_not_found_text.tos_link_text"}) + } else { + v, ok := tf.Attrs["tos_link_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.external_user_not_found_text.tos_link_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.external_user_not_found_text.tos_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosLinkText) == "" + } + v.Value = string(obj.TosLinkText) + v.Unknown = false + tf.Attrs["tos_link_text"] = v + } + } + } + v.Unknown = false + tf.Attrs["external_user_not_found_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["footer_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.footer_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.footer_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["footer_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.FooterText == nil { + v.Null = true + } else { + obj := obj.FooterText + tf := &v + { + t, ok := tf.AttrTypes["help"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.footer_text.help"}) + } else { + v, ok := tf.Attrs["help"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.footer_text.help", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.footer_text.help", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Help) == "" + } + v.Value = string(obj.Help) + v.Unknown = false + tf.Attrs["help"] = v + } + } + { + t, ok := tf.AttrTypes["privacy_policy"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.footer_text.privacy_policy"}) + } else { + v, ok := tf.Attrs["privacy_policy"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.footer_text.privacy_policy", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.footer_text.privacy_policy", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PrivacyPolicy) == "" + } + v.Value = string(obj.PrivacyPolicy) + v.Unknown = false + tf.Attrs["privacy_policy"] = v + } + } + { + t, ok := tf.AttrTypes["tos"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.footer_text.tos"}) + } else { + v, ok := tf.Attrs["tos"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.footer_text.tos", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.footer_text.tos", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Tos) == "" + } + v.Value = string(obj.Tos) + v.Unknown = false + tf.Attrs["tos"] = v + } + } + } + v.Unknown = false + tf.Attrs["footer_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["init_mfa_done_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_done_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_done_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["init_mfa_done_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.InitMfaDoneText == nil { + v.Null = true + } else { + obj := obj.InitMfaDoneText + tf := &v + { + t, ok := tf.AttrTypes["cancel_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_done_text.cancel_button_text"}) + } else { + v, ok := tf.Attrs["cancel_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_done_text.cancel_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CancelButtonText) == "" + } + v.Value = string(obj.CancelButtonText) + v.Unknown = false + tf.Attrs["cancel_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_done_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_done_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_done_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_done_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_done_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_done_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["init_mfa_done_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["init_mfa_otp_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_otp_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_otp_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["init_mfa_otp_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.InitMfaOtpText == nil { + v.Null = true + } else { + obj := obj.InitMfaOtpText + tf := &v + { + t, ok := tf.AttrTypes["cancel_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_otp_text.cancel_button_text"}) + } else { + v, ok := tf.Attrs["cancel_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_otp_text.cancel_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CancelButtonText) == "" + } + v.Value = string(obj.CancelButtonText) + v.Unknown = false + tf.Attrs["cancel_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["code_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_otp_text.code_label"}) + } else { + v, ok := tf.Attrs["code_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_otp_text.code_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.code_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CodeLabel) == "" + } + v.Value = string(obj.CodeLabel) + v.Unknown = false + tf.Attrs["code_label"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_otp_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_otp_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["description_otp"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_otp_text.description_otp"}) + } else { + v, ok := tf.Attrs["description_otp"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_otp_text.description_otp", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.description_otp", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.DescriptionOtp) == "" + } + v.Value = string(obj.DescriptionOtp) + v.Unknown = false + tf.Attrs["description_otp"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_otp_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_otp_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["secret_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_otp_text.secret_label"}) + } else { + v, ok := tf.Attrs["secret_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_otp_text.secret_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.secret_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.SecretLabel) == "" + } + v.Value = string(obj.SecretLabel) + v.Unknown = false + tf.Attrs["secret_label"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_otp_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_otp_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_otp_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["init_mfa_otp_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["init_mfa_prompt_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_prompt_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["init_mfa_prompt_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.InitMfaPromptText == nil { + v.Null = true + } else { + obj := obj.InitMfaPromptText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_prompt_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_prompt_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_prompt_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_prompt_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["otp_option"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_prompt_text.otp_option"}) + } else { + v, ok := tf.Attrs["otp_option"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_prompt_text.otp_option", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.otp_option", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.OtpOption) == "" + } + v.Value = string(obj.OtpOption) + v.Unknown = false + tf.Attrs["otp_option"] = v + } + } + { + t, ok := tf.AttrTypes["skip_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_prompt_text.skip_button_text"}) + } else { + v, ok := tf.Attrs["skip_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_prompt_text.skip_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.skip_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.SkipButtonText) == "" + } + v.Value = string(obj.SkipButtonText) + v.Unknown = false + tf.Attrs["skip_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_prompt_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_prompt_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["u2f_option"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_prompt_text.u2f_option"}) + } else { + v, ok := tf.Attrs["u2f_option"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_prompt_text.u2f_option", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_prompt_text.u2f_option", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.U2FOption) == "" + } + v.Value = string(obj.U2FOption) + v.Unknown = false + tf.Attrs["u2f_option"] = v + } + } + } + v.Unknown = false + tf.Attrs["init_mfa_prompt_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["init_mfa_u2f_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_u2f_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["init_mfa_u2f_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.InitMfaU2FText == nil { + v.Null = true + } else { + obj := obj.InitMfaU2FText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_u2f_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_u2f_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["error_retry"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_u2f_text.error_retry"}) + } else { + v, ok := tf.Attrs["error_retry"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_u2f_text.error_retry", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.error_retry", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ErrorRetry) == "" + } + v.Value = string(obj.ErrorRetry) + v.Unknown = false + tf.Attrs["error_retry"] = v + } + } + { + t, ok := tf.AttrTypes["not_supported"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_u2f_text.not_supported"}) + } else { + v, ok := tf.Attrs["not_supported"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_u2f_text.not_supported", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.not_supported", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NotSupported) == "" + } + v.Value = string(obj.NotSupported) + v.Unknown = false + tf.Attrs["not_supported"] = v + } + } + { + t, ok := tf.AttrTypes["register_token_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_u2f_text.register_token_button_text"}) + } else { + v, ok := tf.Attrs["register_token_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_u2f_text.register_token_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.register_token_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.RegisterTokenButtonText) == "" + } + v.Value = string(obj.RegisterTokenButtonText) + v.Unknown = false + tf.Attrs["register_token_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_u2f_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_u2f_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["token_name_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_mfa_u2f_text.token_name_label"}) + } else { + v, ok := tf.Attrs["token_name_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_mfa_u2f_text.token_name_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_mfa_u2f_text.token_name_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TokenNameLabel) == "" + } + v.Value = string(obj.TokenNameLabel) + v.Unknown = false + tf.Attrs["token_name_label"] = v + } + } + } + v.Unknown = false + tf.Attrs["init_mfa_u2f_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["init_password_done_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_done_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_done_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["init_password_done_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.InitPasswordDoneText == nil { + v.Null = true + } else { + obj := obj.InitPasswordDoneText + tf := &v + { + t, ok := tf.AttrTypes["cancel_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_done_text.cancel_button_text"}) + } else { + v, ok := tf.Attrs["cancel_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_password_done_text.cancel_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CancelButtonText) == "" + } + v.Value = string(obj.CancelButtonText) + v.Unknown = false + tf.Attrs["cancel_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_done_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_password_done_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_done_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_password_done_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_done_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_password_done_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["init_password_done_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["init_password_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["init_password_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.InitPasswordText == nil { + v.Null = true + } else { + obj := obj.InitPasswordText + tf := &v + { + t, ok := tf.AttrTypes["code_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_text.code_label"}) + } else { + v, ok := tf.Attrs["code_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_password_text.code_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_text.code_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CodeLabel) == "" + } + v.Value = string(obj.CodeLabel) + v.Unknown = false + tf.Attrs["code_label"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_password_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["new_password_confirm_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_text.new_password_confirm_label"}) + } else { + v, ok := tf.Attrs["new_password_confirm_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_password_text.new_password_confirm_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_text.new_password_confirm_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NewPasswordConfirmLabel) == "" + } + v.Value = string(obj.NewPasswordConfirmLabel) + v.Unknown = false + tf.Attrs["new_password_confirm_label"] = v + } + } + { + t, ok := tf.AttrTypes["new_password_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_text.new_password_label"}) + } else { + v, ok := tf.Attrs["new_password_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_password_text.new_password_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_text.new_password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NewPasswordLabel) == "" + } + v.Value = string(obj.NewPasswordLabel) + v.Unknown = false + tf.Attrs["new_password_label"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_password_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["resend_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_text.resend_button_text"}) + } else { + v, ok := tf.Attrs["resend_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_password_text.resend_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_text.resend_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ResendButtonText) == "" + } + v.Value = string(obj.ResendButtonText) + v.Unknown = false + tf.Attrs["resend_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.init_password_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.init_password_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.init_password_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["init_password_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["initialize_done_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_done_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_done_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["initialize_done_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.InitializeDoneText == nil { + v.Null = true + } else { + obj := obj.InitializeDoneText + tf := &v + { + t, ok := tf.AttrTypes["cancel_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_done_text.cancel_button_text"}) + } else { + v, ok := tf.Attrs["cancel_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.initialize_done_text.cancel_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CancelButtonText) == "" + } + v.Value = string(obj.CancelButtonText) + v.Unknown = false + tf.Attrs["cancel_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_done_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.initialize_done_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_done_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.initialize_done_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_done_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.initialize_done_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["initialize_done_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["initialize_user_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_user_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_user_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["initialize_user_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.InitializeUserText == nil { + v.Null = true + } else { + obj := obj.InitializeUserText + tf := &v + { + t, ok := tf.AttrTypes["code_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_user_text.code_label"}) + } else { + v, ok := tf.Attrs["code_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.initialize_user_text.code_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_user_text.code_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CodeLabel) == "" + } + v.Value = string(obj.CodeLabel) + v.Unknown = false + tf.Attrs["code_label"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_user_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.initialize_user_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_user_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["new_password_confirm_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_user_text.new_password_confirm_label"}) + } else { + v, ok := tf.Attrs["new_password_confirm_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.initialize_user_text.new_password_confirm_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_user_text.new_password_confirm_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NewPasswordConfirmLabel) == "" + } + v.Value = string(obj.NewPasswordConfirmLabel) + v.Unknown = false + tf.Attrs["new_password_confirm_label"] = v + } + } + { + t, ok := tf.AttrTypes["new_password_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_user_text.new_password_label"}) + } else { + v, ok := tf.Attrs["new_password_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.initialize_user_text.new_password_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_user_text.new_password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NewPasswordLabel) == "" + } + v.Value = string(obj.NewPasswordLabel) + v.Unknown = false + tf.Attrs["new_password_label"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_user_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.initialize_user_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_user_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["resend_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_user_text.resend_button_text"}) + } else { + v, ok := tf.Attrs["resend_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.initialize_user_text.resend_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_user_text.resend_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ResendButtonText) == "" + } + v.Value = string(obj.ResendButtonText) + v.Unknown = false + tf.Attrs["resend_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.initialize_user_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.initialize_user_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.initialize_user_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["initialize_user_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["linking_user_done_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.linking_user_done_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.linking_user_done_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["linking_user_done_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.LinkingUserDoneText == nil { + v.Null = true + } else { + obj := obj.LinkingUserDoneText + tf := &v + { + t, ok := tf.AttrTypes["cancel_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.linking_user_done_text.cancel_button_text"}) + } else { + v, ok := tf.Attrs["cancel_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.linking_user_done_text.cancel_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.linking_user_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CancelButtonText) == "" + } + v.Value = string(obj.CancelButtonText) + v.Unknown = false + tf.Attrs["cancel_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.linking_user_done_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.linking_user_done_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.linking_user_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.linking_user_done_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.linking_user_done_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.linking_user_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.linking_user_done_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.linking_user_done_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.linking_user_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["linking_user_done_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["login_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["login_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.LoginText == nil { + v.Null = true + } else { + obj := obj.LoginText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.login_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["description_linking_process"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text.description_linking_process"}) + } else { + v, ok := tf.Attrs["description_linking_process"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.login_text.description_linking_process", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text.description_linking_process", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.DescriptionLinkingProcess) == "" + } + v.Value = string(obj.DescriptionLinkingProcess) + v.Unknown = false + tf.Attrs["description_linking_process"] = v + } + } + { + t, ok := tf.AttrTypes["external_user_description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text.external_user_description"}) + } else { + v, ok := tf.Attrs["external_user_description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.login_text.external_user_description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text.external_user_description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ExternalUserDescription) == "" + } + v.Value = string(obj.ExternalUserDescription) + v.Unknown = false + tf.Attrs["external_user_description"] = v + } + } + { + t, ok := tf.AttrTypes["login_name_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text.login_name_label"}) + } else { + v, ok := tf.Attrs["login_name_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.login_text.login_name_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text.login_name_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LoginNameLabel) == "" + } + v.Value = string(obj.LoginNameLabel) + v.Unknown = false + tf.Attrs["login_name_label"] = v + } + } + { + t, ok := tf.AttrTypes["login_name_placeholder"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text.login_name_placeholder"}) + } else { + v, ok := tf.Attrs["login_name_placeholder"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.login_text.login_name_placeholder", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text.login_name_placeholder", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LoginNamePlaceholder) == "" + } + v.Value = string(obj.LoginNamePlaceholder) + v.Unknown = false + tf.Attrs["login_name_placeholder"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.login_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["register_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text.register_button_text"}) + } else { + v, ok := tf.Attrs["register_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.login_text.register_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text.register_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.RegisterButtonText) == "" + } + v.Value = string(obj.RegisterButtonText) + v.Unknown = false + tf.Attrs["register_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.login_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["title_linking_process"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text.title_linking_process"}) + } else { + v, ok := tf.Attrs["title_linking_process"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.login_text.title_linking_process", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text.title_linking_process", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TitleLinkingProcess) == "" + } + v.Value = string(obj.TitleLinkingProcess) + v.Unknown = false + tf.Attrs["title_linking_process"] = v + } + } + { + t, ok := tf.AttrTypes["user_must_be_member_of_org"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text.user_must_be_member_of_org"}) + } else { + v, ok := tf.Attrs["user_must_be_member_of_org"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.login_text.user_must_be_member_of_org", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text.user_must_be_member_of_org", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.UserMustBeMemberOfOrg) == "" + } + v.Value = string(obj.UserMustBeMemberOfOrg) + v.Unknown = false + tf.Attrs["user_must_be_member_of_org"] = v + } + } + { + t, ok := tf.AttrTypes["user_name_placeholder"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.login_text.user_name_placeholder"}) + } else { + v, ok := tf.Attrs["user_name_placeholder"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.login_text.user_name_placeholder", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.login_text.user_name_placeholder", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.UserNamePlaceholder) == "" + } + v.Value = string(obj.UserNamePlaceholder) + v.Unknown = false + tf.Attrs["user_name_placeholder"] = v + } + } + } + v.Unknown = false + tf.Attrs["login_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["logout_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.logout_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.logout_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["logout_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.LogoutText == nil { + v.Null = true + } else { + obj := obj.LogoutText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.logout_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.logout_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.logout_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["login_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.logout_text.login_button_text"}) + } else { + v, ok := tf.Attrs["login_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.logout_text.login_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.logout_text.login_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LoginButtonText) == "" + } + v.Value = string(obj.LoginButtonText) + v.Unknown = false + tf.Attrs["login_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.logout_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.logout_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.logout_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["logout_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["mfa_providers_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.mfa_providers_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.mfa_providers_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["mfa_providers_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.MfaProvidersText == nil { + v.Null = true + } else { + obj := obj.MfaProvidersText + tf := &v + { + t, ok := tf.AttrTypes["choose_other"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.mfa_providers_text.choose_other"}) + } else { + v, ok := tf.Attrs["choose_other"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.mfa_providers_text.choose_other", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.mfa_providers_text.choose_other", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ChooseOther) == "" + } + v.Value = string(obj.ChooseOther) + v.Unknown = false + tf.Attrs["choose_other"] = v + } + } + { + t, ok := tf.AttrTypes["otp"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.mfa_providers_text.otp"}) + } else { + v, ok := tf.Attrs["otp"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.mfa_providers_text.otp", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.mfa_providers_text.otp", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Otp) == "" + } + v.Value = string(obj.Otp) + v.Unknown = false + tf.Attrs["otp"] = v + } + } + { + t, ok := tf.AttrTypes["u2f"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.mfa_providers_text.u2f"}) + } else { + v, ok := tf.Attrs["u2f"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.mfa_providers_text.u2f", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.mfa_providers_text.u2f", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.U2F) == "" + } + v.Value = string(obj.U2F) + v.Unknown = false + tf.Attrs["u2f"] = v + } + } + } + v.Unknown = false + tf.Attrs["mfa_providers_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["password_change_done_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_done_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_done_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["password_change_done_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.PasswordChangeDoneText == nil { + v.Null = true + } else { + obj := obj.PasswordChangeDoneText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_done_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_change_done_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_done_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_change_done_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_done_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_change_done_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["password_change_done_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["password_change_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["password_change_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.PasswordChangeText == nil { + v.Null = true + } else { + obj := obj.PasswordChangeText + tf := &v + { + t, ok := tf.AttrTypes["cancel_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_text.cancel_button_text"}) + } else { + v, ok := tf.Attrs["cancel_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_change_text.cancel_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CancelButtonText) == "" + } + v.Value = string(obj.CancelButtonText) + v.Unknown = false + tf.Attrs["cancel_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_change_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["new_password_confirm_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_text.new_password_confirm_label"}) + } else { + v, ok := tf.Attrs["new_password_confirm_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_change_text.new_password_confirm_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_text.new_password_confirm_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NewPasswordConfirmLabel) == "" + } + v.Value = string(obj.NewPasswordConfirmLabel) + v.Unknown = false + tf.Attrs["new_password_confirm_label"] = v + } + } + { + t, ok := tf.AttrTypes["new_password_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_text.new_password_label"}) + } else { + v, ok := tf.Attrs["new_password_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_change_text.new_password_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_text.new_password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NewPasswordLabel) == "" + } + v.Value = string(obj.NewPasswordLabel) + v.Unknown = false + tf.Attrs["new_password_label"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_change_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["old_password_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_text.old_password_label"}) + } else { + v, ok := tf.Attrs["old_password_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_change_text.old_password_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_text.old_password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.OldPasswordLabel) == "" + } + v.Value = string(obj.OldPasswordLabel) + v.Unknown = false + tf.Attrs["old_password_label"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_change_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_change_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_change_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["password_change_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["password_reset_done_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_reset_done_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_reset_done_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["password_reset_done_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.PasswordResetDoneText == nil { + v.Null = true + } else { + obj := obj.PasswordResetDoneText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_reset_done_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_reset_done_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_reset_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_reset_done_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_reset_done_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_reset_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_reset_done_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_reset_done_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_reset_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["password_reset_done_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["password_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["password_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.PasswordText == nil { + v.Null = true + } else { + obj := obj.PasswordText + tf := &v + { + t, ok := tf.AttrTypes["back_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.back_button_text"}) + } else { + v, ok := tf.Attrs["back_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.back_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.back_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.BackButtonText) == "" + } + v.Value = string(obj.BackButtonText) + v.Unknown = false + tf.Attrs["back_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["confirmation"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.confirmation"}) + } else { + v, ok := tf.Attrs["confirmation"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.confirmation", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.confirmation", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Confirmation) == "" + } + v.Value = string(obj.Confirmation) + v.Unknown = false + tf.Attrs["confirmation"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["has_lowercase"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.has_lowercase"}) + } else { + v, ok := tf.Attrs["has_lowercase"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.has_lowercase", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.has_lowercase", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.HasLowercase) == "" + } + v.Value = string(obj.HasLowercase) + v.Unknown = false + tf.Attrs["has_lowercase"] = v + } + } + { + t, ok := tf.AttrTypes["has_number"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.has_number"}) + } else { + v, ok := tf.Attrs["has_number"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.has_number", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.has_number", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.HasNumber) == "" + } + v.Value = string(obj.HasNumber) + v.Unknown = false + tf.Attrs["has_number"] = v + } + } + { + t, ok := tf.AttrTypes["has_symbol"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.has_symbol"}) + } else { + v, ok := tf.Attrs["has_symbol"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.has_symbol", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.has_symbol", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.HasSymbol) == "" + } + v.Value = string(obj.HasSymbol) + v.Unknown = false + tf.Attrs["has_symbol"] = v + } + } + { + t, ok := tf.AttrTypes["has_uppercase"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.has_uppercase"}) + } else { + v, ok := tf.Attrs["has_uppercase"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.has_uppercase", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.has_uppercase", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.HasUppercase) == "" + } + v.Value = string(obj.HasUppercase) + v.Unknown = false + tf.Attrs["has_uppercase"] = v + } + } + { + t, ok := tf.AttrTypes["min_length"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.min_length"}) + } else { + v, ok := tf.Attrs["min_length"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.min_length", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.min_length", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.MinLength) == "" + } + v.Value = string(obj.MinLength) + v.Unknown = false + tf.Attrs["min_length"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["password_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.password_label"}) + } else { + v, ok := tf.Attrs["password_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.password_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PasswordLabel) == "" + } + v.Value = string(obj.PasswordLabel) + v.Unknown = false + tf.Attrs["password_label"] = v + } + } + { + t, ok := tf.AttrTypes["reset_link_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.reset_link_text"}) + } else { + v, ok := tf.Attrs["reset_link_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.reset_link_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.reset_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ResetLinkText) == "" + } + v.Value = string(obj.ResetLinkText) + v.Unknown = false + tf.Attrs["reset_link_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.password_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.password_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.password_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["password_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["passwordless_prompt_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_prompt_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_prompt_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["passwordless_prompt_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.PasswordlessPromptText == nil { + v.Null = true + } else { + obj := obj.PasswordlessPromptText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_prompt_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_prompt_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["description_init"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_prompt_text.description_init"}) + } else { + v, ok := tf.Attrs["description_init"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_prompt_text.description_init", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.description_init", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.DescriptionInit) == "" + } + v.Value = string(obj.DescriptionInit) + v.Unknown = false + tf.Attrs["description_init"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_prompt_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_prompt_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["passwordless_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_prompt_text.passwordless_button_text"}) + } else { + v, ok := tf.Attrs["passwordless_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_prompt_text.passwordless_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.passwordless_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PasswordlessButtonText) == "" + } + v.Value = string(obj.PasswordlessButtonText) + v.Unknown = false + tf.Attrs["passwordless_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["skip_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_prompt_text.skip_button_text"}) + } else { + v, ok := tf.Attrs["skip_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_prompt_text.skip_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.skip_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.SkipButtonText) == "" + } + v.Value = string(obj.SkipButtonText) + v.Unknown = false + tf.Attrs["skip_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_prompt_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_prompt_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_prompt_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["passwordless_prompt_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["passwordless_registration_done_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_done_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["passwordless_registration_done_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.PasswordlessRegistrationDoneText == nil { + v.Null = true + } else { + obj := obj.PasswordlessRegistrationDoneText + tf := &v + { + t, ok := tf.AttrTypes["cancel_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_done_text.cancel_button_text"}) + } else { + v, ok := tf.Attrs["cancel_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_registration_done_text.cancel_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CancelButtonText) == "" + } + v.Value = string(obj.CancelButtonText) + v.Unknown = false + tf.Attrs["cancel_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_done_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_registration_done_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["description_close"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_done_text.description_close"}) + } else { + v, ok := tf.Attrs["description_close"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_registration_done_text.description_close", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text.description_close", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.DescriptionClose) == "" + } + v.Value = string(obj.DescriptionClose) + v.Unknown = false + tf.Attrs["description_close"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_done_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_registration_done_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_done_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_registration_done_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["passwordless_registration_done_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["passwordless_registration_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["passwordless_registration_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.PasswordlessRegistrationText == nil { + v.Null = true + } else { + obj := obj.PasswordlessRegistrationText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_registration_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["error_retry"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_text.error_retry"}) + } else { + v, ok := tf.Attrs["error_retry"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_registration_text.error_retry", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_text.error_retry", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ErrorRetry) == "" + } + v.Value = string(obj.ErrorRetry) + v.Unknown = false + tf.Attrs["error_retry"] = v + } + } + { + t, ok := tf.AttrTypes["not_supported"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_text.not_supported"}) + } else { + v, ok := tf.Attrs["not_supported"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_registration_text.not_supported", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_text.not_supported", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NotSupported) == "" + } + v.Value = string(obj.NotSupported) + v.Unknown = false + tf.Attrs["not_supported"] = v + } + } + { + t, ok := tf.AttrTypes["register_token_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_text.register_token_button_text"}) + } else { + v, ok := tf.Attrs["register_token_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_registration_text.register_token_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_text.register_token_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.RegisterTokenButtonText) == "" + } + v.Value = string(obj.RegisterTokenButtonText) + v.Unknown = false + tf.Attrs["register_token_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_registration_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["token_name_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_registration_text.token_name_label"}) + } else { + v, ok := tf.Attrs["token_name_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_registration_text.token_name_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_registration_text.token_name_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TokenNameLabel) == "" + } + v.Value = string(obj.TokenNameLabel) + v.Unknown = false + tf.Attrs["token_name_label"] = v + } + } + } + v.Unknown = false + tf.Attrs["passwordless_registration_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["passwordless_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["passwordless_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.PasswordlessText == nil { + v.Null = true + } else { + obj := obj.PasswordlessText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["error_retry"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_text.error_retry"}) + } else { + v, ok := tf.Attrs["error_retry"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_text.error_retry", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_text.error_retry", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ErrorRetry) == "" + } + v.Value = string(obj.ErrorRetry) + v.Unknown = false + tf.Attrs["error_retry"] = v + } + } + { + t, ok := tf.AttrTypes["login_with_pw_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_text.login_with_pw_button_text"}) + } else { + v, ok := tf.Attrs["login_with_pw_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_text.login_with_pw_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_text.login_with_pw_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LoginWithPwButtonText) == "" + } + v.Value = string(obj.LoginWithPwButtonText) + v.Unknown = false + tf.Attrs["login_with_pw_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["not_supported"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_text.not_supported"}) + } else { + v, ok := tf.Attrs["not_supported"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_text.not_supported", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_text.not_supported", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NotSupported) == "" + } + v.Value = string(obj.NotSupported) + v.Unknown = false + tf.Attrs["not_supported"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["validate_token_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.passwordless_text.validate_token_button_text"}) + } else { + v, ok := tf.Attrs["validate_token_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.passwordless_text.validate_token_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.passwordless_text.validate_token_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ValidateTokenButtonText) == "" + } + v.Value = string(obj.ValidateTokenButtonText) + v.Unknown = false + tf.Attrs["validate_token_button_text"] = v + } + } + } + v.Unknown = false + tf.Attrs["passwordless_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["registration_option_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_option_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_option_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["registration_option_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.RegistrationOptionText == nil { + v.Null = true + } else { + obj := obj.RegistrationOptionText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_option_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_option_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_option_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["external_login_description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_option_text.external_login_description"}) + } else { + v, ok := tf.Attrs["external_login_description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_option_text.external_login_description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_option_text.external_login_description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ExternalLoginDescription) == "" + } + v.Value = string(obj.ExternalLoginDescription) + v.Unknown = false + tf.Attrs["external_login_description"] = v + } + } + { + t, ok := tf.AttrTypes["login_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_option_text.login_button_text"}) + } else { + v, ok := tf.Attrs["login_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_option_text.login_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_option_text.login_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LoginButtonText) == "" + } + v.Value = string(obj.LoginButtonText) + v.Unknown = false + tf.Attrs["login_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_option_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_option_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_option_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["user_name_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_option_text.user_name_button_text"}) + } else { + v, ok := tf.Attrs["user_name_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_option_text.user_name_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_option_text.user_name_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.UserNameButtonText) == "" + } + v.Value = string(obj.UserNameButtonText) + v.Unknown = false + tf.Attrs["user_name_button_text"] = v + } + } + } + v.Unknown = false + tf.Attrs["registration_option_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["registration_org_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["registration_org_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.RegistrationOrgText == nil { + v.Null = true + } else { + obj := obj.RegistrationOrgText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["email_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.email_label"}) + } else { + v, ok := tf.Attrs["email_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.email_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.email_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.EmailLabel) == "" + } + v.Value = string(obj.EmailLabel) + v.Unknown = false + tf.Attrs["email_label"] = v + } + } + { + t, ok := tf.AttrTypes["firstname_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.firstname_label"}) + } else { + v, ok := tf.Attrs["firstname_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.firstname_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.firstname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.FirstnameLabel) == "" + } + v.Value = string(obj.FirstnameLabel) + v.Unknown = false + tf.Attrs["firstname_label"] = v + } + } + { + t, ok := tf.AttrTypes["lastname_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.lastname_label"}) + } else { + v, ok := tf.Attrs["lastname_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.lastname_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.lastname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LastnameLabel) == "" + } + v.Value = string(obj.LastnameLabel) + v.Unknown = false + tf.Attrs["lastname_label"] = v + } + } + { + t, ok := tf.AttrTypes["orgname_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.orgname_label"}) + } else { + v, ok := tf.Attrs["orgname_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.orgname_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.orgname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.OrgnameLabel) == "" + } + v.Value = string(obj.OrgnameLabel) + v.Unknown = false + tf.Attrs["orgname_label"] = v + } + } + { + t, ok := tf.AttrTypes["password_confirm_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.password_confirm_label"}) + } else { + v, ok := tf.Attrs["password_confirm_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.password_confirm_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.password_confirm_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PasswordConfirmLabel) == "" + } + v.Value = string(obj.PasswordConfirmLabel) + v.Unknown = false + tf.Attrs["password_confirm_label"] = v + } + } + { + t, ok := tf.AttrTypes["password_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.password_label"}) + } else { + v, ok := tf.Attrs["password_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.password_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PasswordLabel) == "" + } + v.Value = string(obj.PasswordLabel) + v.Unknown = false + tf.Attrs["password_label"] = v + } + } + { + t, ok := tf.AttrTypes["privacy_confirm"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.privacy_confirm"}) + } else { + v, ok := tf.Attrs["privacy_confirm"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.privacy_confirm", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.privacy_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PrivacyConfirm) == "" + } + v.Value = string(obj.PrivacyConfirm) + v.Unknown = false + tf.Attrs["privacy_confirm"] = v + } + } + { + t, ok := tf.AttrTypes["privacy_link_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.privacy_link_text"}) + } else { + v, ok := tf.Attrs["privacy_link_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.privacy_link_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.privacy_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PrivacyLinkText) == "" + } + v.Value = string(obj.PrivacyLinkText) + v.Unknown = false + tf.Attrs["privacy_link_text"] = v + } + } + { + t, ok := tf.AttrTypes["save_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.save_button_text"}) + } else { + v, ok := tf.Attrs["save_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.save_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.save_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.SaveButtonText) == "" + } + v.Value = string(obj.SaveButtonText) + v.Unknown = false + tf.Attrs["save_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["tos_and_privacy_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.tos_and_privacy_label"}) + } else { + v, ok := tf.Attrs["tos_and_privacy_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.tos_and_privacy_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.tos_and_privacy_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosAndPrivacyLabel) == "" + } + v.Value = string(obj.TosAndPrivacyLabel) + v.Unknown = false + tf.Attrs["tos_and_privacy_label"] = v + } + } + { + t, ok := tf.AttrTypes["tos_confirm"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.tos_confirm"}) + } else { + v, ok := tf.Attrs["tos_confirm"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.tos_confirm", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.tos_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosConfirm) == "" + } + v.Value = string(obj.TosConfirm) + v.Unknown = false + tf.Attrs["tos_confirm"] = v + } + } + { + t, ok := tf.AttrTypes["tos_link_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.tos_link_text"}) + } else { + v, ok := tf.Attrs["tos_link_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.tos_link_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.tos_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosLinkText) == "" + } + v.Value = string(obj.TosLinkText) + v.Unknown = false + tf.Attrs["tos_link_text"] = v + } + } + { + t, ok := tf.AttrTypes["username_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_org_text.username_label"}) + } else { + v, ok := tf.Attrs["username_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_org_text.username_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_org_text.username_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.UsernameLabel) == "" + } + v.Value = string(obj.UsernameLabel) + v.Unknown = false + tf.Attrs["username_label"] = v + } + } + } + v.Unknown = false + tf.Attrs["registration_org_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["registration_user_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["registration_user_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.RegistrationUserText == nil { + v.Null = true + } else { + obj := obj.RegistrationUserText + tf := &v + { + t, ok := tf.AttrTypes["back_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.back_button_text"}) + } else { + v, ok := tf.Attrs["back_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.back_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.back_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.BackButtonText) == "" + } + v.Value = string(obj.BackButtonText) + v.Unknown = false + tf.Attrs["back_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["description_org_register"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.description_org_register"}) + } else { + v, ok := tf.Attrs["description_org_register"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.description_org_register", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.description_org_register", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.DescriptionOrgRegister) == "" + } + v.Value = string(obj.DescriptionOrgRegister) + v.Unknown = false + tf.Attrs["description_org_register"] = v + } + } + { + t, ok := tf.AttrTypes["email_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.email_label"}) + } else { + v, ok := tf.Attrs["email_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.email_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.email_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.EmailLabel) == "" + } + v.Value = string(obj.EmailLabel) + v.Unknown = false + tf.Attrs["email_label"] = v + } + } + { + t, ok := tf.AttrTypes["firstname_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.firstname_label"}) + } else { + v, ok := tf.Attrs["firstname_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.firstname_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.firstname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.FirstnameLabel) == "" + } + v.Value = string(obj.FirstnameLabel) + v.Unknown = false + tf.Attrs["firstname_label"] = v + } + } + { + t, ok := tf.AttrTypes["gender_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.gender_label"}) + } else { + v, ok := tf.Attrs["gender_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.gender_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.gender_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.GenderLabel) == "" + } + v.Value = string(obj.GenderLabel) + v.Unknown = false + tf.Attrs["gender_label"] = v + } + } + { + t, ok := tf.AttrTypes["language_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.language_label"}) + } else { + v, ok := tf.Attrs["language_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.language_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.language_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LanguageLabel) == "" + } + v.Value = string(obj.LanguageLabel) + v.Unknown = false + tf.Attrs["language_label"] = v + } + } + { + t, ok := tf.AttrTypes["lastname_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.lastname_label"}) + } else { + v, ok := tf.Attrs["lastname_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.lastname_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.lastname_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.LastnameLabel) == "" + } + v.Value = string(obj.LastnameLabel) + v.Unknown = false + tf.Attrs["lastname_label"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["password_confirm_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.password_confirm_label"}) + } else { + v, ok := tf.Attrs["password_confirm_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.password_confirm_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.password_confirm_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PasswordConfirmLabel) == "" + } + v.Value = string(obj.PasswordConfirmLabel) + v.Unknown = false + tf.Attrs["password_confirm_label"] = v + } + } + { + t, ok := tf.AttrTypes["password_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.password_label"}) + } else { + v, ok := tf.Attrs["password_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.password_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.password_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PasswordLabel) == "" + } + v.Value = string(obj.PasswordLabel) + v.Unknown = false + tf.Attrs["password_label"] = v + } + } + { + t, ok := tf.AttrTypes["privacy_confirm"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.privacy_confirm"}) + } else { + v, ok := tf.Attrs["privacy_confirm"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.privacy_confirm", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.privacy_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PrivacyConfirm) == "" + } + v.Value = string(obj.PrivacyConfirm) + v.Unknown = false + tf.Attrs["privacy_confirm"] = v + } + } + { + t, ok := tf.AttrTypes["privacy_link_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.privacy_link_text"}) + } else { + v, ok := tf.Attrs["privacy_link_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.privacy_link_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.privacy_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PrivacyLinkText) == "" + } + v.Value = string(obj.PrivacyLinkText) + v.Unknown = false + tf.Attrs["privacy_link_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["tos_and_privacy_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.tos_and_privacy_label"}) + } else { + v, ok := tf.Attrs["tos_and_privacy_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.tos_and_privacy_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.tos_and_privacy_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosAndPrivacyLabel) == "" + } + v.Value = string(obj.TosAndPrivacyLabel) + v.Unknown = false + tf.Attrs["tos_and_privacy_label"] = v + } + } + { + t, ok := tf.AttrTypes["tos_confirm"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.tos_confirm"}) + } else { + v, ok := tf.Attrs["tos_confirm"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.tos_confirm", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.tos_confirm", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosConfirm) == "" + } + v.Value = string(obj.TosConfirm) + v.Unknown = false + tf.Attrs["tos_confirm"] = v + } + } + { + t, ok := tf.AttrTypes["tos_link_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.tos_link_text"}) + } else { + v, ok := tf.Attrs["tos_link_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.tos_link_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.tos_link_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TosLinkText) == "" + } + v.Value = string(obj.TosLinkText) + v.Unknown = false + tf.Attrs["tos_link_text"] = v + } + } + { + t, ok := tf.AttrTypes["username_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.registration_user_text.username_label"}) + } else { + v, ok := tf.Attrs["username_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.registration_user_text.username_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.registration_user_text.username_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.UsernameLabel) == "" + } + v.Value = string(obj.UsernameLabel) + v.Unknown = false + tf.Attrs["username_label"] = v + } + } + } + v.Unknown = false + tf.Attrs["registration_user_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["select_account_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.select_account_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.select_account_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["select_account_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.SelectAccountText == nil { + v.Null = true + } else { + obj := obj.SelectAccountText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.select_account_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.select_account_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.select_account_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["description_linking_process"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.select_account_text.description_linking_process"}) + } else { + v, ok := tf.Attrs["description_linking_process"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.select_account_text.description_linking_process", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.select_account_text.description_linking_process", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.DescriptionLinkingProcess) == "" + } + v.Value = string(obj.DescriptionLinkingProcess) + v.Unknown = false + tf.Attrs["description_linking_process"] = v + } + } + { + t, ok := tf.AttrTypes["other_user"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.select_account_text.other_user"}) + } else { + v, ok := tf.Attrs["other_user"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.select_account_text.other_user", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.select_account_text.other_user", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.OtherUser) == "" + } + v.Value = string(obj.OtherUser) + v.Unknown = false + tf.Attrs["other_user"] = v + } + } + { + t, ok := tf.AttrTypes["session_state_active"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.select_account_text.session_state_active"}) + } else { + v, ok := tf.Attrs["session_state_active"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.select_account_text.session_state_active", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.select_account_text.session_state_active", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.SessionStateActive) == "" + } + v.Value = string(obj.SessionStateActive) + v.Unknown = false + tf.Attrs["session_state_active"] = v + } + } + { + t, ok := tf.AttrTypes["session_state_inactive"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.select_account_text.session_state_inactive"}) + } else { + v, ok := tf.Attrs["session_state_inactive"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.select_account_text.session_state_inactive", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.select_account_text.session_state_inactive", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.SessionStateInactive) == "" + } + v.Value = string(obj.SessionStateInactive) + v.Unknown = false + tf.Attrs["session_state_inactive"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.select_account_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.select_account_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.select_account_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["title_linking_process"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.select_account_text.title_linking_process"}) + } else { + v, ok := tf.Attrs["title_linking_process"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.select_account_text.title_linking_process", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.select_account_text.title_linking_process", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.TitleLinkingProcess) == "" + } + v.Value = string(obj.TitleLinkingProcess) + v.Unknown = false + tf.Attrs["title_linking_process"] = v + } + } + { + t, ok := tf.AttrTypes["user_must_be_member_of_org"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.select_account_text.user_must_be_member_of_org"}) + } else { + v, ok := tf.Attrs["user_must_be_member_of_org"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.select_account_text.user_must_be_member_of_org", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.select_account_text.user_must_be_member_of_org", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.UserMustBeMemberOfOrg) == "" + } + v.Value = string(obj.UserMustBeMemberOfOrg) + v.Unknown = false + tf.Attrs["user_must_be_member_of_org"] = v + } + } + } + v.Unknown = false + tf.Attrs["select_account_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["success_login_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.success_login_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.success_login_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["success_login_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.SuccessLoginText == nil { + v.Null = true + } else { + obj := obj.SuccessLoginText + tf := &v + { + t, ok := tf.AttrTypes["auto_redirect_description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.success_login_text.auto_redirect_description"}) + } else { + v, ok := tf.Attrs["auto_redirect_description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.success_login_text.auto_redirect_description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.success_login_text.auto_redirect_description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.AutoRedirectDescription) == "" + } + v.Value = string(obj.AutoRedirectDescription) + v.Unknown = false + tf.Attrs["auto_redirect_description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.success_login_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.success_login_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.success_login_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["redirected_description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.success_login_text.redirected_description"}) + } else { + v, ok := tf.Attrs["redirected_description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.success_login_text.redirected_description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.success_login_text.redirected_description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.RedirectedDescription) == "" + } + v.Value = string(obj.RedirectedDescription) + v.Unknown = false + tf.Attrs["redirected_description"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.success_login_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.success_login_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.success_login_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["success_login_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["username_change_done_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.username_change_done_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.username_change_done_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["username_change_done_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.UsernameChangeDoneText == nil { + v.Null = true + } else { + obj := obj.UsernameChangeDoneText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.username_change_done_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.username_change_done_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.username_change_done_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.username_change_done_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.username_change_done_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.username_change_done_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.username_change_done_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.username_change_done_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.username_change_done_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["username_change_done_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["username_change_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.username_change_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.username_change_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["username_change_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.UsernameChangeText == nil { + v.Null = true + } else { + obj := obj.UsernameChangeText + tf := &v + { + t, ok := tf.AttrTypes["cancel_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.username_change_text.cancel_button_text"}) + } else { + v, ok := tf.Attrs["cancel_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.username_change_text.cancel_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.username_change_text.cancel_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CancelButtonText) == "" + } + v.Value = string(obj.CancelButtonText) + v.Unknown = false + tf.Attrs["cancel_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.username_change_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.username_change_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.username_change_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.username_change_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.username_change_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.username_change_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.username_change_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.username_change_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.username_change_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["username_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.username_change_text.username_label"}) + } else { + v, ok := tf.Attrs["username_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.username_change_text.username_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.username_change_text.username_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.UsernameLabel) == "" + } + v.Value = string(obj.UsernameLabel) + v.Unknown = false + tf.Attrs["username_label"] = v + } + } + } + v.Unknown = false + tf.Attrs["username_change_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["verify_mfa_otp_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.verify_mfa_otp_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.verify_mfa_otp_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["verify_mfa_otp_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.VerifyMfaOtpText == nil { + v.Null = true + } else { + obj := obj.VerifyMfaOtpText + tf := &v + { + t, ok := tf.AttrTypes["code_label"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.verify_mfa_otp_text.code_label"}) + } else { + v, ok := tf.Attrs["code_label"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.verify_mfa_otp_text.code_label", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.verify_mfa_otp_text.code_label", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.CodeLabel) == "" + } + v.Value = string(obj.CodeLabel) + v.Unknown = false + tf.Attrs["code_label"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.verify_mfa_otp_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.verify_mfa_otp_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.verify_mfa_otp_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["next_button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.verify_mfa_otp_text.next_button_text"}) + } else { + v, ok := tf.Attrs["next_button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.verify_mfa_otp_text.next_button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.verify_mfa_otp_text.next_button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NextButtonText) == "" + } + v.Value = string(obj.NextButtonText) + v.Unknown = false + tf.Attrs["next_button_text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.verify_mfa_otp_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.verify_mfa_otp_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.verify_mfa_otp_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["verify_mfa_otp_text"] = v + } + } + } + { + a, ok := tf.AttrTypes["verify_mfa_u2f_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.verify_mfa_u2f_text"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["verify_mfa_u2f_text"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.VerifyMfaU2FText == nil { + v.Null = true + } else { + obj := obj.VerifyMfaU2FText + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.verify_mfa_u2f_text.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.verify_mfa_u2f_text.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + t, ok := tf.AttrTypes["error_retry"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.verify_mfa_u2f_text.error_retry"}) + } else { + v, ok := tf.Attrs["error_retry"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.verify_mfa_u2f_text.error_retry", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text.error_retry", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ErrorRetry) == "" + } + v.Value = string(obj.ErrorRetry) + v.Unknown = false + tf.Attrs["error_retry"] = v + } + } + { + t, ok := tf.AttrTypes["not_supported"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.verify_mfa_u2f_text.not_supported"}) + } else { + v, ok := tf.Attrs["not_supported"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.verify_mfa_u2f_text.not_supported", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text.not_supported", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.NotSupported) == "" + } + v.Value = string(obj.NotSupported) + v.Unknown = false + tf.Attrs["not_supported"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.verify_mfa_u2f_text.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.verify_mfa_u2f_text.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + { + t, ok := tf.AttrTypes["validate_token_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"LoginCustomText.verify_mfa_u2f_text.validate_token_text"}) + } else { + v, ok := tf.Attrs["validate_token_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"LoginCustomText.verify_mfa_u2f_text.validate_token_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"LoginCustomText.verify_mfa_u2f_text.validate_token_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ValidateTokenText) == "" + } + v.Value = string(obj.ValidateTokenText) + v.Unknown = false + tf.Attrs["validate_token_text"] = v + } + } + } + v.Unknown = false + tf.Attrs["verify_mfa_u2f_text"] = v + } + } + } + return diags +} + +// CopyMessageCustomTextFromTerraform copies contents of the source Terraform object into a target struct +func CopyMessageCustomTextFromTerraform(_ context.Context, tf github_com_hashicorp_terraform_plugin_framework_types.Object, obj *textpb.MessageCustomText) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + { + a, ok := tf.Attrs["button_text"] + if !ok { + diags.Append(attrReadMissingDiag{"MessageCustomText.button_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"MessageCustomText.button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.ButtonText = t + } + } + } + { + a, ok := tf.Attrs["footer_text"] + if !ok { + diags.Append(attrReadMissingDiag{"MessageCustomText.footer_text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"MessageCustomText.footer_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.FooterText = t + } + } + } + { + a, ok := tf.Attrs["greeting"] + if !ok { + diags.Append(attrReadMissingDiag{"MessageCustomText.greeting"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"MessageCustomText.greeting", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Greeting = t + } + } + } + { + a, ok := tf.Attrs["pre_header"] + if !ok { + diags.Append(attrReadMissingDiag{"MessageCustomText.pre_header"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"MessageCustomText.pre_header", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.PreHeader = t + } + } + } + { + a, ok := tf.Attrs["subject"] + if !ok { + diags.Append(attrReadMissingDiag{"MessageCustomText.subject"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"MessageCustomText.subject", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Subject = t + } + } + } + { + a, ok := tf.Attrs["text"] + if !ok { + diags.Append(attrReadMissingDiag{"MessageCustomText.text"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"MessageCustomText.text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Text = t + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"MessageCustomText.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"MessageCustomText.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + return diags +} + +// CopyMessageCustomTextToTerraform copies contents of the source Terraform object into a target struct +func CopyMessageCustomTextToTerraform(ctx context.Context, obj textpb.MessageCustomText, tf *github_com_hashicorp_terraform_plugin_framework_types.Object) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + tf.Null = false + tf.Unknown = false + if tf.Attrs == nil { + tf.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value) + } + { + t, ok := tf.AttrTypes["button_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"MessageCustomText.button_text"}) + } else { + v, ok := tf.Attrs["button_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"MessageCustomText.button_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"MessageCustomText.button_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.ButtonText) == "" + } + v.Value = string(obj.ButtonText) + v.Unknown = false + tf.Attrs["button_text"] = v + } + } + { + t, ok := tf.AttrTypes["footer_text"] + if !ok { + diags.Append(attrWriteMissingDiag{"MessageCustomText.footer_text"}) + } else { + v, ok := tf.Attrs["footer_text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"MessageCustomText.footer_text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"MessageCustomText.footer_text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.FooterText) == "" + } + v.Value = string(obj.FooterText) + v.Unknown = false + tf.Attrs["footer_text"] = v + } + } + { + t, ok := tf.AttrTypes["greeting"] + if !ok { + diags.Append(attrWriteMissingDiag{"MessageCustomText.greeting"}) + } else { + v, ok := tf.Attrs["greeting"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"MessageCustomText.greeting", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"MessageCustomText.greeting", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Greeting) == "" + } + v.Value = string(obj.Greeting) + v.Unknown = false + tf.Attrs["greeting"] = v + } + } + { + t, ok := tf.AttrTypes["pre_header"] + if !ok { + diags.Append(attrWriteMissingDiag{"MessageCustomText.pre_header"}) + } else { + v, ok := tf.Attrs["pre_header"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"MessageCustomText.pre_header", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"MessageCustomText.pre_header", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.PreHeader) == "" + } + v.Value = string(obj.PreHeader) + v.Unknown = false + tf.Attrs["pre_header"] = v + } + } + { + t, ok := tf.AttrTypes["subject"] + if !ok { + diags.Append(attrWriteMissingDiag{"MessageCustomText.subject"}) + } else { + v, ok := tf.Attrs["subject"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"MessageCustomText.subject", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"MessageCustomText.subject", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Subject) == "" + } + v.Value = string(obj.Subject) + v.Unknown = false + tf.Attrs["subject"] = v + } + } + { + t, ok := tf.AttrTypes["text"] + if !ok { + diags.Append(attrWriteMissingDiag{"MessageCustomText.text"}) + } else { + v, ok := tf.Attrs["text"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"MessageCustomText.text", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"MessageCustomText.text", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Text) == "" + } + v.Value = string(obj.Text) + v.Unknown = false + tf.Attrs["text"] = v + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"MessageCustomText.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"MessageCustomText.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"MessageCustomText.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + return diags +} + +// attrReadMissingDiag represents diagnostic message on an attribute missing in the source object +type attrReadMissingDiag struct { + Path string +} + +func (d attrReadMissingDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrReadMissingDiag) Summary() string { + return "Error reading from Terraform object" +} + +func (d attrReadMissingDiag) Detail() string { + return fmt.Sprintf("A value for %v is missing in the source Terraform object Attrs", d.Path) +} + +func (d attrReadMissingDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrReadConversionFailureDiag represents diagnostic message on a failed type conversion on read +type attrReadConversionFailureDiag struct { + Path string + Type string +} + +func (d attrReadConversionFailureDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrReadConversionFailureDiag) Summary() string { + return "Error reading from Terraform object" +} + +func (d attrReadConversionFailureDiag) Detail() string { + return fmt.Sprintf("A value for %v can not be converted to %v", d.Path, d.Type) +} + +func (d attrReadConversionFailureDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteMissingDiag represents diagnostic message on an attribute missing in the target object +type attrWriteMissingDiag struct { + Path string +} + +func (d attrWriteMissingDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteMissingDiag) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteMissingDiag) Detail() string { + return fmt.Sprintf("A value for %v is missing in the source Terraform object AttrTypes", d.Path) +} + +func (d attrWriteMissingDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteConversionFailureDiag represents diagnostic message on a failed type conversion on write +type attrWriteConversionFailureDiag struct { + Path string + Type string +} + +func (d attrWriteConversionFailureDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteConversionFailureDiag) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteConversionFailureDiag) Detail() string { + return fmt.Sprintf("A value for %v can not be converted to %v", d.Path, d.Type) +} + +func (d attrWriteConversionFailureDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteGeneralError represents diagnostic message on a generic error on write +type attrWriteGeneralError struct { + Path string + Err error +} + +func (d attrWriteGeneralError) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteGeneralError) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteGeneralError) Detail() string { + return fmt.Sprintf("%s: %s", d.Path, d.Err.Error()) +} + +func (d attrWriteGeneralError) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} diff --git a/go.mod b/go.mod index e62db688..080b0662 100644 --- a/go.mod +++ b/go.mod @@ -1,61 +1,73 @@ module github.com/zitadel/terraform-provider-zitadel -go 1.17 +go 1.20 require ( - github.com/hashicorp/terraform-plugin-log v0.4.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.16.0 - github.com/zitadel/oidc v1.5.1 - github.com/zitadel/zitadel-go v0.3.5 - github.com/zitadel/zitadel-go/v2 v2.0.0-v2-alpha.2 - google.golang.org/protobuf v1.28.0 + github.com/envoyproxy/protoc-gen-validate v1.0.2 + github.com/gabriel-vasile/mimetype v1.4.1 + github.com/gogo/protobuf v1.3.2 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 + github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 + github.com/hashicorp/hcl/v2 v2.16.2 + github.com/hashicorp/terraform-plugin-framework v0.15.0 + github.com/hashicorp/terraform-plugin-go v0.14.3 + github.com/hashicorp/terraform-plugin-log v0.8.0 + github.com/hashicorp/terraform-plugin-mux v0.7.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 + github.com/zclconf/go-cty v1.13.1 + github.com/zitadel/oidc v1.13.4 + github.com/zitadel/zitadel-go/v2 v2.0.15 + golang.org/x/oauth2 v0.10.0 + google.golang.org/grpc v1.57.0-dev + google.golang.org/protobuf v1.31.0 ) require ( - github.com/agext/levenshtein v1.2.2 // indirect + github.com/agext/levenshtein v1.2.3 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect - github.com/envoyproxy/protoc-gen-validate v0.6.7 // indirect - github.com/fatih/color v1.7.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/go-test/deep v1.0.7 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/gorilla/schema v1.2.0 // indirect github.com/gorilla/securecookie v1.1.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.3 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect - github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect - github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-checkpoint v0.5.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.4.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.3 // indirect + github.com/hashicorp/go-plugin v1.4.8 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.4.0 // indirect - github.com/hashicorp/hcl/v2 v2.12.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/hc-install v0.5.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-plugin-go v0.9.0 // indirect - github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 // indirect + github.com/hashicorp/terraform-exec v0.18.1 // indirect + github.com/hashicorp/terraform-json v0.16.0 // indirect + github.com/hashicorp/terraform-registry-address v0.1.0 // indirect github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect - github.com/kr/pretty v0.2.1 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/mattn/go-colorable v0.1.4 // indirect - github.com/mattn/go-isatty v0.0.10 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/go-wordwrap v1.0.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/run v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/stretchr/testify v1.8.4 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect github.com/vmihailenco/tagparser v0.1.1 // indirect - github.com/zclconf/go-cty v1.10.0 // indirect - golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect - golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect - golang.org/x/sys v0.0.0-20220207234003-57398862261d // indirect - golang.org/x/text v0.3.7 // indirect - google.golang.org/appengine v1.6.6 // indirect - google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect - google.golang.org/grpc v1.47.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/mod v0.11.0 // indirect + golang.org/x/net v0.12.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect ) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..aa84c1fe --- /dev/null +++ b/go.sum @@ -0,0 +1,338 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= +github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= +github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q= +github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= +github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= +github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= +github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= +github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= +github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= +github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= +github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= +github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= +github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hc-install v0.5.0 h1:D9bl4KayIYKEeJ4vUDe9L5huqxZXczKaykSRcmQ0xY0= +github.com/hashicorp/hc-install v0.5.0/go.mod h1:JyzMfbzfSBSjoDCRPna1vi/24BEDxFaCPfdHtM5SCdo= +github.com/hashicorp/hcl/v2 v2.16.2 h1:mpkHZh/Tv+xet3sy3F9Ld4FyI2tUpWe9x3XtPx9f1a0= +github.com/hashicorp/hcl/v2 v2.16.2/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/terraform-exec v0.18.1 h1:LAbfDvNQU1l0NOQlTuudjczVhHj061fNX5H8XZxHlH4= +github.com/hashicorp/terraform-exec v0.18.1/go.mod h1:58wg4IeuAJ6LVsLUeD2DWZZoc/bYi6dzhLHzxM41980= +github.com/hashicorp/terraform-json v0.16.0 h1:UKkeWRWb23do5LNAFlh/K3N0ymn1qTOO8c+85Albo3s= +github.com/hashicorp/terraform-json v0.16.0/go.mod h1:v0Ufk9jJnk6tcIZvScHvetlKfiNTC+WS21mnXIlc0B0= +github.com/hashicorp/terraform-plugin-framework v0.15.0 h1:6f4UY2yfp5UsSX9JhUA6RSptjd+ojStBGWA4jrPhB6Q= +github.com/hashicorp/terraform-plugin-framework v0.15.0/go.mod h1:wcZdk4+Uef6Ng+BiBJjGAcIPlIs5bhlEV/TA1k6Xkq8= +github.com/hashicorp/terraform-plugin-go v0.14.3 h1:nlnJ1GXKdMwsC8g1Nh05tK2wsC3+3BL/DBBxFEki+j0= +github.com/hashicorp/terraform-plugin-go v0.14.3/go.mod h1:7ees7DMZ263q8wQ6E4RdIdR6nHHJtrdt4ogX5lPkX1A= +github.com/hashicorp/terraform-plugin-log v0.8.0 h1:pX2VQ/TGKu+UU1rCay0OlzosNKe4Nz1pepLXj95oyy0= +github.com/hashicorp/terraform-plugin-log v0.8.0/go.mod h1:1myFrhVsBLeylQzYYEV17VVjtG8oYPRFdaZs7xdW2xs= +github.com/hashicorp/terraform-plugin-mux v0.7.0 h1:wRbSYzg+v2sn5Mdee0UKm4YTt4wJG0LfSwtgNuBkglY= +github.com/hashicorp/terraform-plugin-mux v0.7.0/go.mod h1:Ae30Mc5lz4d1awtiCbHP0YyvgBeiQ00Q1nAq0U3lb+I= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 h1:G9WAfb8LHeCxu7Ae8nc1agZlQOSCUWsb610iAogBhCs= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1/go.mod h1:xcOSYlRVdPLmDUoqPhO9fiO/YCN/l6MGYeTzGt5jgkQ= +github.com/hashicorp/terraform-registry-address v0.1.0 h1:W6JkV9wbum+m516rCl5/NjKxCyTVaaUBbzYcMzBDO3U= +github.com/hashicorp/terraform-registry-address v0.1.0/go.mod h1:EnyO2jYO6j29DTHbJcm00E5nQTFeTtyZH3H5ycydQ5A= +github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= +github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= +github.com/zclconf/go-cty v1.13.1 h1:0a6bRwuiSHtAmqCqNOE+c2oHgepv0ctoxU4FUe43kwc= +github.com/zclconf/go-cty v1.13.1/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= +github.com/zitadel/oidc v1.13.4 h1:+k2GKqP9Ld9S2MSFlj+KaNsoZ3J9oy+Ezw51EzSFuC8= +github.com/zitadel/oidc v1.13.4/go.mod h1:3h2DhUcP02YV6q/CA/BG4yla0o6rXjK+DkJGK/dwJfw= +github.com/zitadel/zitadel-go/v2 v2.0.15 h1:dL2VROG7w32xtdl2DlPqpef6+s7PEP70KEa3SEjGsuI= +github.com/zitadel/zitadel-go/v2 v2.0.15/go.mod h1:2xpHigKocm9hVdNuAHo9eway/jGSo6XhyunbfGr7924= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e h1:Ao9GzfUMPH3zjVfzXG5rlWlk+Q8MXWKwWpwVQE1MXfw= +google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 h1:DEH99RbiLZhMxrpEJCZ0A+wdTe0EOgou/poSLx9vWf4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.57.0-dev h1:XCvzqke2TBzQUs3a1MO1VsqVkWeAdJ/KmxHCAVtPpe8= +google.golang.org/grpc v1.57.0-dev/go.mod h1:ZPf89/axrdgRDfHqb7fa0GF5t4VUER0vWnqnPNuRP7k= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 2822cd7f..0596299a 100644 --- a/main.go +++ b/main.go @@ -1,22 +1,48 @@ package main import ( + "context" "flag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" + "log" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server" + "github.com/hashicorp/terraform-plugin-mux/tf5to6server" + "github.com/hashicorp/terraform-plugin-mux/tf6muxserver" + "github.com/zitadel/terraform-provider-zitadel/zitadel" ) func main() { + ctx := context.Background() var debug bool flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") flag.Parse() - plugin.Serve(&plugin.ServeOpts{ - Debug: debug, - ProviderFunc: func() *schema.Provider { - return zitadel.Provider() + upgradedSdkProvider, err := tf5to6server.UpgradeServer(ctx, zitadel.Provider().GRPCProvider) + if err != nil { + log.Fatal(err) + } + + providers := []func() tfprotov6.ProviderServer{ + func() tfprotov6.ProviderServer { + return upgradedSdkProvider }, - }) + providerserver.NewProtocol6(zitadel.NewProviderPV6()), + } + + muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...) + + opts := []tf6server.ServeOpt{} + if debug { + opts = append(opts, tf6server.WithManagedDebug()) + } + + err = tf6server.Serve("registry.terraform.io/zitadel/zitadel", muxServer.ProviderServer, opts...) + + if err != nil { + log.Fatalln(err.Error()) + } } diff --git a/templates/data-sources/action.md.tmpl b/templates/data-sources/action.md.tmpl new file mode 100644 index 00000000..ab867881 --- /dev/null +++ b/templates/data-sources/action.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/action.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/application_api.md.tmpl b/templates/data-sources/application_api.md.tmpl new file mode 100644 index 00000000..c9b313cb --- /dev/null +++ b/templates/data-sources/application_api.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/application_api.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/application_oidc.md.tmpl b/templates/data-sources/application_oidc.md.tmpl new file mode 100644 index 00000000..b29e2473 --- /dev/null +++ b/templates/data-sources/application_oidc.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/application_oidc.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/default_oidc_settings.md.tmpl b/templates/data-sources/default_oidc_settings.md.tmpl new file mode 100644 index 00000000..be8aa77e --- /dev/null +++ b/templates/data-sources/default_oidc_settings.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/default_oidc_settings.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/human_user.md.tmpl b/templates/data-sources/human_user.md.tmpl new file mode 100644 index 00000000..c4268810 --- /dev/null +++ b/templates/data-sources/human_user.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/human_user.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/idp_azure_ad.md.tmpl b/templates/data-sources/idp_azure_ad.md.tmpl new file mode 100644 index 00000000..0aaf0c0d --- /dev/null +++ b/templates/data-sources/idp_azure_ad.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/idp_azure_ad.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/idp_github.md.tmpl b/templates/data-sources/idp_github.md.tmpl new file mode 100644 index 00000000..94baab8e --- /dev/null +++ b/templates/data-sources/idp_github.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/idp_github.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/idp_github_es.md.tmpl b/templates/data-sources/idp_github_es.md.tmpl new file mode 100644 index 00000000..2f07f6b3 --- /dev/null +++ b/templates/data-sources/idp_github_es.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/idp_github_es.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/idp_gitlab.md.tmpl b/templates/data-sources/idp_gitlab.md.tmpl new file mode 100644 index 00000000..48bdafc4 --- /dev/null +++ b/templates/data-sources/idp_gitlab.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/idp_gitlab.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/idp_gitlab_self_hosted.md.tmpl b/templates/data-sources/idp_gitlab_self_hosted.md.tmpl new file mode 100644 index 00000000..8475746d --- /dev/null +++ b/templates/data-sources/idp_gitlab_self_hosted.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/idp_gitlab_self_hosted.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/idp_google.md.tmpl b/templates/data-sources/idp_google.md.tmpl new file mode 100644 index 00000000..dde90fce --- /dev/null +++ b/templates/data-sources/idp_google.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/idp_google.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/idp_ldap.md.tmpl b/templates/data-sources/idp_ldap.md.tmpl new file mode 100644 index 00000000..362dd199 --- /dev/null +++ b/templates/data-sources/idp_ldap.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/idp_ldap.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/machine_user.md.tmpl b/templates/data-sources/machine_user.md.tmpl new file mode 100644 index 00000000..ab82e401 --- /dev/null +++ b/templates/data-sources/machine_user.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/machine_user.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/org.md.tmpl b/templates/data-sources/org.md.tmpl new file mode 100644 index 00000000..a359cdac --- /dev/null +++ b/templates/data-sources/org.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/org.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/org_idp_azure_ad.md.tmpl b/templates/data-sources/org_idp_azure_ad.md.tmpl new file mode 100644 index 00000000..90084566 --- /dev/null +++ b/templates/data-sources/org_idp_azure_ad.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/org_idp_azure_ad.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/org_idp_github.md.tmpl b/templates/data-sources/org_idp_github.md.tmpl new file mode 100644 index 00000000..245ccbc9 --- /dev/null +++ b/templates/data-sources/org_idp_github.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/org_idp_github.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/org_idp_github_es.md.tmpl b/templates/data-sources/org_idp_github_es.md.tmpl new file mode 100644 index 00000000..c75102e7 --- /dev/null +++ b/templates/data-sources/org_idp_github_es.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/org_idp_github_es.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/org_idp_gitlab.md.tmpl b/templates/data-sources/org_idp_gitlab.md.tmpl new file mode 100644 index 00000000..570d85b2 --- /dev/null +++ b/templates/data-sources/org_idp_gitlab.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/org_idp_gitlab.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/org_idp_gitlab_self_hosted.md.tmpl b/templates/data-sources/org_idp_gitlab_self_hosted.md.tmpl new file mode 100644 index 00000000..58f3624b --- /dev/null +++ b/templates/data-sources/org_idp_gitlab_self_hosted.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/org_idp_gitlab_self_hosted.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/org_idp_google.md.tmpl b/templates/data-sources/org_idp_google.md.tmpl new file mode 100644 index 00000000..a7c65747 --- /dev/null +++ b/templates/data-sources/org_idp_google.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/org_idp_google.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/org_idp_ldap.md.tmpl b/templates/data-sources/org_idp_ldap.md.tmpl new file mode 100644 index 00000000..832967e7 --- /dev/null +++ b/templates/data-sources/org_idp_ldap.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/org_idp_ldap.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/data-sources/org_jwt_idp.md.tmpl b/templates/data-sources/org_jwt_idp.md.tmpl new file mode 100644 index 00000000..c1ab5a4c --- /dev/null +++ b/templates/data-sources/org_jwt_idp.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/org_jwt_idp.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/org_oidc_idp.md.tmpl b/templates/data-sources/org_oidc_idp.md.tmpl new file mode 100644 index 00000000..e5a18a4d --- /dev/null +++ b/templates/data-sources/org_oidc_idp.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/org_oidc_idp.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/project.md.tmpl b/templates/data-sources/project.md.tmpl new file mode 100644 index 00000000..e35a898f --- /dev/null +++ b/templates/data-sources/project.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/project.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/project_role.md.tmpl b/templates/data-sources/project_role.md.tmpl new file mode 100644 index 00000000..ade179f5 --- /dev/null +++ b/templates/data-sources/project_role.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/project_role.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/trigger_actions.md.tmpl b/templates/data-sources/trigger_actions.md.tmpl new file mode 100644 index 00000000..90dc4c13 --- /dev/null +++ b/templates/data-sources/trigger_actions.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/data-sources/trigger_actions.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl new file mode 100644 index 00000000..5c10e7af --- /dev/null +++ b/templates/index.md.tmpl @@ -0,0 +1,31 @@ +--- +page_title: "Provider: ZITADEL" +description: |- + Provider to manage resources on ZITADEL + +--- + +# {{ .ProviderShortName | upper }} Provider + +This provider allows the management of different resources on a ZITADEL instance. + +Through this provider it is possible to interact with the [ZITADEL API](https://docs.zitadel.com/docs/apis/introduction) to configure different aspects of the instance. + +Currently does not contain, but could be included in the future if demand exists: + +- instance as resource +- policies and settings on instance level as resource +- custom text resources + +Not planned to include as resources, as more administration task as a resource: + +- Import and export of organizations +- (re-)setting of password for human user +- initializing of OTP setup for human user +- initializing of passwordless setup for human user + +## Configuring the provider + +{{ tffile "examples/provider/provider.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/resources/action.md.tmpl b/templates/resources/action.md.tmpl new file mode 100644 index 00000000..f7c825f8 --- /dev/null +++ b/templates/resources/action.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/action.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/action-import.sh" }} diff --git a/templates/resources/application_api.md.tmpl b/templates/resources/application_api.md.tmpl new file mode 100644 index 00000000..c4c1d3f9 --- /dev/null +++ b/templates/resources/application_api.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/application_api.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/application_api-import.sh" }} diff --git a/templates/resources/application_key.md.tmpl b/templates/resources/application_key.md.tmpl new file mode 100644 index 00000000..b0a02f48 --- /dev/null +++ b/templates/resources/application_key.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/application_key.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/application_key-import.sh" }} diff --git a/templates/resources/application_oidc.md.tmpl b/templates/resources/application_oidc.md.tmpl new file mode 100644 index 00000000..e49c20a4 --- /dev/null +++ b/templates/resources/application_oidc.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/application_oidc.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/application_oidc-import.sh" }} diff --git a/templates/resources/default_domain_claimed_message_text.md.tmpl b/templates/resources/default_domain_claimed_message_text.md.tmpl new file mode 100644 index 00000000..8328db47 --- /dev/null +++ b/templates/resources/default_domain_claimed_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_domain_claimed_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/default_domain_policy.md.tmpl b/templates/resources/default_domain_policy.md.tmpl new file mode 100644 index 00000000..453fe77f --- /dev/null +++ b/templates/resources/default_domain_policy.md.tmpl @@ -0,0 +1,21 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_domain_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + + +{{ tffile "examples/provider/resources/default_domain_policy-import.sh" }} diff --git a/templates/resources/default_init_message_text.md.tmpl b/templates/resources/default_init_message_text.md.tmpl new file mode 100644 index 00000000..77e66953 --- /dev/null +++ b/templates/resources/default_init_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_init_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/default_label_policy.md.tmpl b/templates/resources/default_label_policy.md.tmpl new file mode 100644 index 00000000..b0774023 --- /dev/null +++ b/templates/resources/default_label_policy.md.tmpl @@ -0,0 +1,21 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_label_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + + +{{ tffile "examples/provider/resources/default_label_policy-import.sh" }} diff --git a/templates/resources/default_lockout_policy.md.tmpl b/templates/resources/default_lockout_policy.md.tmpl new file mode 100644 index 00000000..714c7b34 --- /dev/null +++ b/templates/resources/default_lockout_policy.md.tmpl @@ -0,0 +1,21 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_lockout_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + + +{{ tffile "examples/provider/resources/default_lockout_policy-import.sh" }} diff --git a/templates/resources/default_login_policy.md.tmpl b/templates/resources/default_login_policy.md.tmpl new file mode 100644 index 00000000..9dd7303f --- /dev/null +++ b/templates/resources/default_login_policy.md.tmpl @@ -0,0 +1,21 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_login_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + + +{{ tffile "examples/provider/resources/default_login_policy-import.sh" }} diff --git a/templates/resources/default_login_texts.md.tmpl b/templates/resources/default_login_texts.md.tmpl new file mode 100644 index 00000000..094156bf --- /dev/null +++ b/templates/resources/default_login_texts.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_login_texts.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/resources/default_notification_policy.md.tmpl b/templates/resources/default_notification_policy.md.tmpl new file mode 100644 index 00000000..2d541a1d --- /dev/null +++ b/templates/resources/default_notification_policy.md.tmpl @@ -0,0 +1,21 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_notification_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + + +{{ tffile "examples/provider/resources/default_notification_policy-import.sh" }} diff --git a/templates/resources/default_oidc_settings.md.tmpl b/templates/resources/default_oidc_settings.md.tmpl new file mode 100644 index 00000000..3d377910 --- /dev/null +++ b/templates/resources/default_oidc_settings.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_oidc_settings.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/resources/default_password_change_message_text.md.tmpl b/templates/resources/default_password_change_message_text.md.tmpl new file mode 100644 index 00000000..4fa4e210 --- /dev/null +++ b/templates/resources/default_password_change_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_password_change_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/resources/default_password_complexity_policy.md.tmpl b/templates/resources/default_password_complexity_policy.md.tmpl new file mode 100644 index 00000000..264fdb53 --- /dev/null +++ b/templates/resources/default_password_complexity_policy.md.tmpl @@ -0,0 +1,21 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_password_complexity_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + + +{{ tffile "examples/provider/resources/default_password_complexity_policy-import.sh" }} diff --git a/templates/resources/default_password_reset_message_text.md.tmpl b/templates/resources/default_password_reset_message_text.md.tmpl new file mode 100644 index 00000000..143b5f6c --- /dev/null +++ b/templates/resources/default_password_reset_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_password_reset_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/default_passwordless_registration_message_text.md.tmpl b/templates/resources/default_passwordless_registration_message_text.md.tmpl new file mode 100644 index 00000000..c5cc4e95 --- /dev/null +++ b/templates/resources/default_passwordless_registration_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_passwordless_registration_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/default_privacy_policy.md.tmpl b/templates/resources/default_privacy_policy.md.tmpl new file mode 100644 index 00000000..da618548 --- /dev/null +++ b/templates/resources/default_privacy_policy.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_privacy_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/default_privacy_policy-import.sh" }} diff --git a/templates/resources/default_verify_email_message_text.md.tmpl b/templates/resources/default_verify_email_message_text.md.tmpl new file mode 100644 index 00000000..dc5fd52f --- /dev/null +++ b/templates/resources/default_verify_email_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_verify_email_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/default_verify_phone_message_text.md.tmpl b/templates/resources/default_verify_phone_message_text.md.tmpl new file mode 100644 index 00000000..46315f47 --- /dev/null +++ b/templates/resources/default_verify_phone_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/default_verify_phone_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/domain.md.tmpl b/templates/resources/domain.md.tmpl new file mode 100644 index 00000000..29e0a63f --- /dev/null +++ b/templates/resources/domain.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/domain.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/domain-import.sh" }} diff --git a/templates/resources/domain_claimed_message_text.md.tmpl b/templates/resources/domain_claimed_message_text.md.tmpl new file mode 100644 index 00000000..ead94351 --- /dev/null +++ b/templates/resources/domain_claimed_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/domain_claimed_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/domain_policy.md.tmpl b/templates/resources/domain_policy.md.tmpl new file mode 100644 index 00000000..27b470ed --- /dev/null +++ b/templates/resources/domain_policy.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/domain_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/domain_policy-import.sh" }} diff --git a/templates/resources/human_user.md.tmpl b/templates/resources/human_user.md.tmpl new file mode 100644 index 00000000..341eae3d --- /dev/null +++ b/templates/resources/human_user.md.tmpl @@ -0,0 +1,22 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +**Caution: Email can only be set verified if a password is set for the user, either with initial_password or during runtime** + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/human_user.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/human_user-import.sh" }} diff --git a/templates/resources/idp_azure_ad.md.tmpl b/templates/resources/idp_azure_ad.md.tmpl new file mode 100644 index 00000000..2d117e5c --- /dev/null +++ b/templates/resources/idp_azure_ad.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/idp_azure_ad.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/idp_azure_ad-import.sh" }} diff --git a/templates/resources/idp_github.md.tmpl b/templates/resources/idp_github.md.tmpl new file mode 100644 index 00000000..43b4c8ca --- /dev/null +++ b/templates/resources/idp_github.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/idp_github.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/idp_github-import.sh" }} diff --git a/templates/resources/idp_github_es.md.tmpl b/templates/resources/idp_github_es.md.tmpl new file mode 100644 index 00000000..7f929c52 --- /dev/null +++ b/templates/resources/idp_github_es.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/idp_github_es.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/idp_github_es-import.sh" }} diff --git a/templates/resources/idp_gitlab.md.tmpl b/templates/resources/idp_gitlab.md.tmpl new file mode 100644 index 00000000..0a961df4 --- /dev/null +++ b/templates/resources/idp_gitlab.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/idp_gitlab.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/idp_gitlab-import.sh" }} diff --git a/templates/resources/idp_gitlab_self_hosted.md.tmpl b/templates/resources/idp_gitlab_self_hosted.md.tmpl new file mode 100644 index 00000000..5c71bcdf --- /dev/null +++ b/templates/resources/idp_gitlab_self_hosted.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/idp_gitlab_self_hosted.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/idp_gitlab_self_hosted-import.sh" }} diff --git a/templates/resources/idp_google.md.tmpl b/templates/resources/idp_google.md.tmpl new file mode 100644 index 00000000..e4dd9568 --- /dev/null +++ b/templates/resources/idp_google.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/idp_google.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/idp_google-import.sh" }} diff --git a/templates/resources/idp_ldap.md.tmpl b/templates/resources/idp_ldap.md.tmpl new file mode 100644 index 00000000..7267d83d --- /dev/null +++ b/templates/resources/idp_ldap.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/idp_ldap.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/idp_ldap-import.sh" }} diff --git a/templates/resources/init_message_text.md.tmpl b/templates/resources/init_message_text.md.tmpl new file mode 100644 index 00000000..3adec240 --- /dev/null +++ b/templates/resources/init_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/init_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/instance_member.md.tmpl b/templates/resources/instance_member.md.tmpl new file mode 100644 index 00000000..8e4517e9 --- /dev/null +++ b/templates/resources/instance_member.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/instance_member.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/instance_member-import.sh" }} diff --git a/templates/resources/label_policy.md.tmpl b/templates/resources/label_policy.md.tmpl new file mode 100644 index 00000000..a578d2b6 --- /dev/null +++ b/templates/resources/label_policy.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/label_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/label_policy-import.sh" }} diff --git a/templates/resources/lockout_policy.md.tmpl b/templates/resources/lockout_policy.md.tmpl new file mode 100644 index 00000000..df6a7553 --- /dev/null +++ b/templates/resources/lockout_policy.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/lockout_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/lockout_policy-import.sh" }} diff --git a/templates/resources/login_policy.md.tmpl b/templates/resources/login_policy.md.tmpl new file mode 100644 index 00000000..6716725d --- /dev/null +++ b/templates/resources/login_policy.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/login_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/login_policy-import.sh" }} diff --git a/templates/resources/login_texts.md.tmpl b/templates/resources/login_texts.md.tmpl new file mode 100644 index 00000000..1cc7ec62 --- /dev/null +++ b/templates/resources/login_texts.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/login_texts.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/machine_key.md.tmpl b/templates/resources/machine_key.md.tmpl new file mode 100644 index 00000000..37db7cf1 --- /dev/null +++ b/templates/resources/machine_key.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/machine_key.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/machine_key-import.sh" }} diff --git a/templates/resources/machine_user.md.tmpl b/templates/resources/machine_user.md.tmpl new file mode 100644 index 00000000..943dd71e --- /dev/null +++ b/templates/resources/machine_user.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/machine_user.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/machine_user-import.sh" }} diff --git a/templates/resources/notification_policy.md.tmpl b/templates/resources/notification_policy.md.tmpl new file mode 100644 index 00000000..6bfee305 --- /dev/null +++ b/templates/resources/notification_policy.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/notification_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/notification_policy-import.sh" }} diff --git a/templates/resources/org.md.tmpl b/templates/resources/org.md.tmpl new file mode 100644 index 00000000..dfa864a7 --- /dev/null +++ b/templates/resources/org.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/org.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/org-import.sh" }} diff --git a/templates/resources/org_idp_azure_ad.md.tmpl b/templates/resources/org_idp_azure_ad.md.tmpl new file mode 100644 index 00000000..faf0c2e5 --- /dev/null +++ b/templates/resources/org_idp_azure_ad.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/org_idp_azure_ad.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/org_idp_azure_ad-import.sh" }} diff --git a/templates/resources/org_idp_github.md.tmpl b/templates/resources/org_idp_github.md.tmpl new file mode 100644 index 00000000..96f92c8f --- /dev/null +++ b/templates/resources/org_idp_github.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/org_idp_github.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/org_idp_github-import.sh" }} diff --git a/templates/resources/org_idp_github_es.md.tmpl b/templates/resources/org_idp_github_es.md.tmpl new file mode 100644 index 00000000..abc5b1da --- /dev/null +++ b/templates/resources/org_idp_github_es.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/org_idp_github_es.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/org_idp_github_es-import.sh" }} diff --git a/templates/resources/org_idp_gitlab.md.tmpl b/templates/resources/org_idp_gitlab.md.tmpl new file mode 100644 index 00000000..11f5f876 --- /dev/null +++ b/templates/resources/org_idp_gitlab.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/org_idp_gitlab.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/org_idp_gitlab-import.sh" }} diff --git a/templates/resources/org_idp_gitlab_self_hosted.md.tmpl b/templates/resources/org_idp_gitlab_self_hosted.md.tmpl new file mode 100644 index 00000000..41166408 --- /dev/null +++ b/templates/resources/org_idp_gitlab_self_hosted.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/org_idp_gitlab_self_hosted.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/org_idp_gitlab_self_hosted-import.sh" }} diff --git a/templates/resources/org_idp_google.md.tmpl b/templates/resources/org_idp_google.md.tmpl new file mode 100644 index 00000000..342387a3 --- /dev/null +++ b/templates/resources/org_idp_google.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/org_idp_google.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/org_idp_google-import.sh" }} diff --git a/templates/resources/org_idp_jwt.md.tmpl b/templates/resources/org_idp_jwt.md.tmpl new file mode 100644 index 00000000..d5c1d058 --- /dev/null +++ b/templates/resources/org_idp_jwt.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/org_idp_jwt.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/org_idp_jwt-import.sh" }} diff --git a/templates/resources/org_idp_ldap.md.tmpl b/templates/resources/org_idp_ldap.md.tmpl new file mode 100644 index 00000000..b16bc21f --- /dev/null +++ b/templates/resources/org_idp_ldap.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/org_idp_ldap.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/org_idp_ldap-import.sh" }} diff --git a/templates/resources/org_idp_oidc.md.tmpl b/templates/resources/org_idp_oidc.md.tmpl new file mode 100644 index 00000000..b149e23f --- /dev/null +++ b/templates/resources/org_idp_oidc.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/org_idp_oidc.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/org_idp_oidc-import.sh" }} diff --git a/templates/resources/org_member.md.tmpl b/templates/resources/org_member.md.tmpl new file mode 100644 index 00000000..fcdc6b0b --- /dev/null +++ b/templates/resources/org_member.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/org_member.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/org_member-import.sh" }} diff --git a/templates/resources/password_change_message_text.md.tmpl b/templates/resources/password_change_message_text.md.tmpl new file mode 100644 index 00000000..5a150cfb --- /dev/null +++ b/templates/resources/password_change_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/password_change_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/resources/password_complexity_policy.md.tmpl b/templates/resources/password_complexity_policy.md.tmpl new file mode 100644 index 00000000..ca1593bf --- /dev/null +++ b/templates/resources/password_complexity_policy.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/password_complexity_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/password_reset_message_text.md.tmpl b/templates/resources/password_reset_message_text.md.tmpl new file mode 100644 index 00000000..66bdbbff --- /dev/null +++ b/templates/resources/password_reset_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/password_reset_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/passwordless_registration_message_text.md.tmpl b/templates/resources/passwordless_registration_message_text.md.tmpl new file mode 100644 index 00000000..b998aaec --- /dev/null +++ b/templates/resources/passwordless_registration_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/passwordless_registration_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/personal_access_token.md.tmpl b/templates/resources/personal_access_token.md.tmpl new file mode 100644 index 00000000..b2dbc99b --- /dev/null +++ b/templates/resources/personal_access_token.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/personal_access_token.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/personal_access_token-import.sh" }} diff --git a/templates/resources/privacy_policy.md.tmpl b/templates/resources/privacy_policy.md.tmpl new file mode 100644 index 00000000..4021dc11 --- /dev/null +++ b/templates/resources/privacy_policy.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/privacy_policy.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/privacy_policy-import.sh" }} diff --git a/templates/resources/project.md.tmpl b/templates/resources/project.md.tmpl new file mode 100644 index 00000000..495e13c3 --- /dev/null +++ b/templates/resources/project.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/project.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/project-import.sh" }} diff --git a/templates/resources/project_grant.md.tmpl b/templates/resources/project_grant.md.tmpl new file mode 100644 index 00000000..83cc1456 --- /dev/null +++ b/templates/resources/project_grant.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/project_grant.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/project_grant-import.sh" }} diff --git a/templates/resources/project_grant_member.md.tmpl b/templates/resources/project_grant_member.md.tmpl new file mode 100644 index 00000000..d4392be7 --- /dev/null +++ b/templates/resources/project_grant_member.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/project_grant_member.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/project_grant_member-import.sh" }} diff --git a/templates/resources/project_member.md.tmpl b/templates/resources/project_member.md.tmpl new file mode 100644 index 00000000..e3fcb0cd --- /dev/null +++ b/templates/resources/project_member.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/project_member.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/project_member-import.sh" }} diff --git a/templates/resources/project_role.md.tmpl b/templates/resources/project_role.md.tmpl new file mode 100644 index 00000000..dcfa76e8 --- /dev/null +++ b/templates/resources/project_role.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/project_role.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/project_role-import.sh" }} diff --git a/templates/resources/sms_provider_twilio.md.tmpl b/templates/resources/sms_provider_twilio.md.tmpl new file mode 100644 index 00000000..914cbd25 --- /dev/null +++ b/templates/resources/sms_provider_twilio.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/sms_provider_twilio.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/sms_provider_twilio-import.sh" }} diff --git a/templates/resources/smtp_config.md.tmpl b/templates/resources/smtp_config.md.tmpl new file mode 100644 index 00000000..f54e2d43 --- /dev/null +++ b/templates/resources/smtp_config.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/smtp_config.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/smtp_config-import.sh" }} diff --git a/templates/resources/trigger_actions.md.tmpl b/templates/resources/trigger_actions.md.tmpl new file mode 100644 index 00000000..f941b99d --- /dev/null +++ b/templates/resources/trigger_actions.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/trigger_actions.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/trigger_actions-import.sh" }} diff --git a/templates/resources/user_grant.md.tmpl b/templates/resources/user_grant.md.tmpl new file mode 100644 index 00000000..2c3d79dd --- /dev/null +++ b/templates/resources/user_grant.md.tmpl @@ -0,0 +1,20 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/user_grant.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +{{ tffile "examples/provider/resources/user_grant-import.sh" }} diff --git a/templates/resources/verify_email_message_text.md.tmpl b/templates/resources/verify_email_message_text.md.tmpl new file mode 100644 index 00000000..5b8a8b08 --- /dev/null +++ b/templates/resources/verify_email_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/verify_email_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/verify_phone_message_text.md.tmpl b/templates/resources/verify_phone_message_text.md.tmpl new file mode 100644 index 00000000..d471b6c7 --- /dev/null +++ b/templates/resources/verify_phone_message_text.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/provider/resources/verify_phone_message_text.tf" }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/terraform-registry-manifest.json b/terraform-registry-manifest.json new file mode 100644 index 00000000..8f423732 --- /dev/null +++ b/terraform-registry-manifest.json @@ -0,0 +1,8 @@ +{ + "version": 1, + "metadata": { + "protocol_versions": [ + "6.0" + ] + } +} \ No newline at end of file diff --git a/zitadel/action/action_test_dep/dependency.go b/zitadel/action/action_test_dep/dependency.go new file mode 100644 index 00000000..bbe6ee26 --- /dev/null +++ b/zitadel/action/action_test_dep/dependency.go @@ -0,0 +1,24 @@ +package action_test_dep + +import ( + "testing" + "time" + + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/action" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func Create(t *testing.T, frame *test_utils.OrgTestFrame) (string, string) { + return test_utils.CreateDefaultDependency(t, "zitadel_action", action.ActionIDVar, func() (string, error) { + a, err := frame.CreateAction(frame, &management.CreateActionRequest{ + Name: frame.UniqueResourcesID, + Script: "not a script", + Timeout: durationpb.New(10 * time.Second), + AllowedToFail: true, + }) + return a.GetId(), err + }) +} diff --git a/zitadel/action/const.go b/zitadel/action/const.go new file mode 100644 index 00000000..2fa536eb --- /dev/null +++ b/zitadel/action/const.go @@ -0,0 +1,10 @@ +package action + +const ( + ActionIDVar = "action_id" + stateVar = "state" + NameVar = "name" + ScriptVar = "script" + timeoutVar = "timeout" + allowedToFailVar = "allowed_to_fail" +) diff --git a/zitadel/action/datasource.go b/zitadel/action/datasource.go new file mode 100644 index 00000000..5ce4c760 --- /dev/null +++ b/zitadel/action/datasource.go @@ -0,0 +1,47 @@ +package action + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing an action belonging to an organization.", + Schema: map[string]*schema.Schema{ + ActionIDVar: { + Type: schema.TypeString, + Required: true, + Description: "The ID of this resource.", + }, + helper.OrgIDVar: helper.OrgIDResourceField, + stateVar: { + Type: schema.TypeInt, + Computed: true, + Description: "the state of the action", + }, + NameVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + ScriptVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + timeoutVar: { + Type: schema.TypeString, + Computed: true, + Description: "after which time the action will be terminated if not finished", + }, + allowedToFailVar: { + Type: schema.TypeBool, + Computed: true, + Description: "when true, the next action will be called even if this action fails", + }, + }, + ReadContext: read, + } +} diff --git a/zitadel/action/funcs.go b/zitadel/action/funcs.go new file mode 100644 index 00000000..53f7bc17 --- /dev/null +++ b/zitadel/action/funcs.go @@ -0,0 +1,152 @@ +package action + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/action" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + timeout, err := time.ParseDuration(d.Get(timeoutVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateAction(ctx, &management.UpdateActionRequest{ + Id: d.Id(), + Name: d.Get(NameVar).(string), + Script: d.Get(ScriptVar).(string), + Timeout: durationpb.New(timeout), + AllowedToFail: d.Get(allowedToFailVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to update action: %v", err) + } + return nil +} + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.DeleteAction(ctx, &management.DeleteActionRequest{ + Id: d.Id(), + }) + if err != nil { + return diag.Errorf("failed to delete action: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + timeout, err := time.ParseDuration(d.Get(timeoutVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.CreateAction(ctx, &management.CreateActionRequest{ + Name: d.Get(NameVar).(string), + Script: d.Get(ScriptVar).(string), + Timeout: durationpb.New(timeout), + AllowedToFail: d.Get(allowedToFailVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to create action: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.ListActions(ctx, &management.ListActionsRequest{ + Queries: []*management.ActionQuery{ + {Query: &management.ActionQuery_ActionIdQuery{ + ActionIdQuery: &action.ActionIDQuery{ + Id: helper.GetID(d, ActionIDVar), + }, + }}, + }, + }) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to list actions") + } + + if len(resp.Result) == 1 { + action := resp.Result[0] + set := map[string]interface{}{ + helper.OrgIDVar: action.GetDetails().GetResourceOwner(), + NameVar: action.GetName(), + stateVar: action.GetState(), + ScriptVar: action.GetScript(), + timeoutVar: action.GetTimeout().AsDuration().String(), + allowedToFailVar: action.GetAllowedToFail(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of action: %v", k, err) + } + } + d.SetId(action.GetId()) + return nil + } + + d.SetId("") + return nil +} diff --git a/zitadel/action/resource.go b/zitadel/action/resource.go new file mode 100644 index 00000000..f88b0754 --- /dev/null +++ b/zitadel/action/resource.go @@ -0,0 +1,50 @@ +package action + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing an action belonging to an organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + stateVar: { + Type: schema.TypeInt, + Computed: true, + Description: "the state of the action", + /* Not necessary as long as only active actions are created + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return EnumValueValidation(actionState, value, action.ActionState_value) + },*/ + }, + NameVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + ScriptVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + timeoutVar: { + Type: schema.TypeString, + Required: true, + Description: "after which time the action will be terminated if not finished", + }, + allowedToFailVar: { + Type: schema.TypeBool, + Required: true, + Description: "when true, the next action will be called even if this action fails", + }, + }, + CreateContext: create, + DeleteContext: delete, + ReadContext: read, + UpdateContext: update, + Importer: helper.ImportWithIDAndOptionalOrg(ActionIDVar), + } +} diff --git a/zitadel/action/resource_test.go b/zitadel/action/resource_test.go new file mode 100644 index 00000000..c71ec072 --- /dev/null +++ b/zitadel/action/resource_test.go @@ -0,0 +1,55 @@ +package action_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/action" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccAction(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_action") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + // name must be unique + nameAttribute := test_utils.AttributeValue(t, action.NameVar, exampleAttributes).AsString() + resourceExample = strings.Replace(resourceExample, nameAttribute, frame.UniqueResourcesID, 1) + exampleProperty := test_utils.AttributeValue(t, action.ScriptVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedproperty", + "", "", "", + true, + checkRemoteProperty(frame), + test_utils.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(frame), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetAction(frame, &management.GetActionRequest{Id: frame.State(state).ID}) + if err != nil { + return err + } + actual := remoteResource.GetAction().GetScript() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/application_api/application_api_test_dep/dependency.go b/zitadel/application_api/application_api_test_dep/dependency.go new file mode 100644 index 00000000..4215885a --- /dev/null +++ b/zitadel/application_api/application_api_test_dep/dependency.go @@ -0,0 +1,22 @@ +package application_api_test_dep + +import ( + "testing" + + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/app" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/application_api" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func Create(t *testing.T, frame *test_utils.OrgTestFrame, projectID string) (string, string) { + return test_utils.CreateDefaultDependency(t, "zitadel_application_api", application_api.AppIDVar, func() (string, error) { + apiApp, err := frame.AddAPIApp(frame, &management.AddAPIAppRequest{ + ProjectId: projectID, + Name: frame.UniqueResourcesID, + AuthMethodType: app.APIAuthMethodType_API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT, + }) + return apiApp.GetAppId(), err + }) +} diff --git a/zitadel/application_api/const.go b/zitadel/application_api/const.go new file mode 100644 index 00000000..c1c211a4 --- /dev/null +++ b/zitadel/application_api/const.go @@ -0,0 +1,10 @@ +package application_api + +const ( + AppIDVar = "app_id" + ProjectIDVar = "project_id" + NameVar = "name" + authMethodTypeVar = "auth_method_type" + ClientIDVar = "client_id" + ClientSecretVar = "client_secret" +) diff --git a/zitadel/application_api/datasource.go b/zitadel/application_api/datasource.go new file mode 100644 index 00000000..08a91496 --- /dev/null +++ b/zitadel/application_api/datasource.go @@ -0,0 +1,37 @@ +package application_api + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing an API application belonging to a project, with all configuration possibilities.", + Schema: map[string]*schema.Schema{ + AppIDVar: { + Type: schema.TypeString, + Required: true, + Description: "The ID of this resource.", + }, + helper.OrgIDVar: helper.OrgIDDatasourceField, + ProjectIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the project", + }, + NameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Name of the application", + }, + authMethodTypeVar: { + Type: schema.TypeString, + Computed: true, + Description: "Auth method type", + }, + }, + ReadContext: read, + } +} diff --git a/zitadel/application_api/funcs.go b/zitadel/application_api/funcs.go new file mode 100644 index 00000000..efb610e7 --- /dev/null +++ b/zitadel/application_api/funcs.go @@ -0,0 +1,147 @@ +package application_api + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/app" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveApp(ctx, &management.RemoveAppRequest{ + ProjectId: d.Get(ProjectIDVar).(string), + AppId: d.Id(), + }) + if err != nil { + return diag.Errorf("failed to delete applicationAPI: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + projectID := d.Get(ProjectIDVar).(string) + if d.HasChange(NameVar) { + _, err = client.UpdateApp(ctx, &management.UpdateAppRequest{ + ProjectId: projectID, + AppId: d.Id(), + Name: d.Get(NameVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update application: %v", err) + } + } + + if d.HasChanges(authMethodTypeVar) { + _, err = client.UpdateAPIAppConfig(ctx, &management.UpdateAPIAppConfigRequest{ + ProjectId: projectID, + AppId: d.Id(), + AuthMethodType: app.APIAuthMethodType(app.APIAuthMethodType_value[d.Get(authMethodTypeVar).(string)]), + }) + if err != nil { + return diag.Errorf("failed to update applicationAPI: %v", err) + } + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.AddAPIApp(ctx, &management.AddAPIAppRequest{ + ProjectId: d.Get(ProjectIDVar).(string), + Name: d.Get(NameVar).(string), + AuthMethodType: app.APIAuthMethodType(app.APIAuthMethodType_value[(d.Get(authMethodTypeVar).(string))]), + }) + + set := map[string]interface{}{ + ClientIDVar: resp.GetClientId(), + ClientSecretVar: resp.GetClientSecret(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of applicationAPI: %v", k, err) + } + } + if err != nil { + return diag.Errorf("failed to create applicationAPI: %v", err) + } + d.SetId(resp.GetAppId()) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetAppByID(ctx, &management.GetAppByIDRequest{ProjectId: d.Get(ProjectIDVar).(string), AppId: helper.GetID(d, AppIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get application api") + } + + app := resp.GetApp() + api := app.GetApiConfig() + set := map[string]interface{}{ + helper.OrgIDVar: app.GetDetails().GetResourceOwner(), + NameVar: app.GetName(), + authMethodTypeVar: api.GetAuthMethodType().String(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of applicationAPI: %v", k, err) + } + } + d.SetId(app.GetId()) + return nil +} diff --git a/zitadel/application_api/resource.go b/zitadel/application_api/resource.go new file mode 100644 index 00000000..a95443e3 --- /dev/null +++ b/zitadel/application_api/resource.go @@ -0,0 +1,61 @@ +package application_api + +import ( + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/app" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing an API application belonging to a project, with all configuration possibilities.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + ProjectIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the project", + ForceNew: true, + }, + NameVar: { + Type: schema.TypeString, + Required: true, + Description: "Name of the application", + }, + authMethodTypeVar: { + Type: schema.TypeString, + Optional: true, + Description: "Auth method type" + helper.DescriptionEnumValuesList(app.APIAuthMethodType_name), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(authMethodTypeVar, value, app.APIAuthMethodType_value) + }, + Default: app.APIAuthMethodType_name[0], + }, + ClientIDVar: { + Type: schema.TypeString, + Computed: true, + Description: "generated ID for this config", + Sensitive: true, + }, + ClientSecretVar: { + Type: schema.TypeString, + Computed: true, + Description: "generated secret for this config", + Sensitive: true, + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithIDAndOptionalOrg( + AppIDVar, + helper.NewImportAttribute(ProjectIDVar, helper.ConvertID, false), + helper.NewImportAttribute(ClientIDVar, helper.ConvertNonEmpty, true), + helper.NewImportAttribute(ClientSecretVar, helper.ConvertNonEmpty, true), + ), + } +} diff --git a/zitadel/application_api/resource_test.go b/zitadel/application_api/resource_test.go new file mode 100644 index 00000000..cd482699 --- /dev/null +++ b/zitadel/application_api/resource_test.go @@ -0,0 +1,57 @@ +package application_api_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/application_api" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project/project_test_dep" +) + +func TestAccAppAPI(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_application_api") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, application_api.NameVar, exampleAttributes).AsString() + projectDep, projectID := project_test_dep.Create(t, frame) + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, projectDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedproperty", + "", "", "", + false, + checkRemoteProperty(frame, projectID), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(frame, projectID), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, application_api.ProjectIDVar), + test_utils.ImportOrgId(frame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, application_api.ClientIDVar), + test_utils.ImportStateAttribute(frame.BaseTestFrame, application_api.ClientSecretVar), + ), + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, projectId string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetAppByID(frame, &management.GetAppByIDRequest{AppId: frame.State(state).ID, ProjectId: projectId}) + if err != nil { + return err + } + actual := remoteResource.GetApp().GetName() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/application_key/const.go b/zitadel/application_key/const.go new file mode 100644 index 00000000..d9d32ada --- /dev/null +++ b/zitadel/application_key/const.go @@ -0,0 +1,10 @@ +package application_key + +const ( + keyIDVar = "key_id" + ProjectIDVar = "project_id" + AppIDVar = "app_id" + keyTypeVar = "key_type" + KeyDetailsVar = "key_details" + ExpirationDateVar = "expiration_date" +) diff --git a/zitadel/application_key/funcs.go b/zitadel/application_key/funcs.go new file mode 100644 index 00000000..afeb9a88 --- /dev/null +++ b/zitadel/application_key/funcs.go @@ -0,0 +1,123 @@ +package application_key + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/authn" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveAppKey(ctx, &management.RemoveAppKeyRequest{ + ProjectId: d.Get(ProjectIDVar).(string), + AppId: d.Get(AppIDVar).(string), + KeyId: d.Id(), + }) + if err != nil { + return diag.Errorf("failed to delete app key: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + orgID := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, orgID) + if err != nil { + return diag.FromErr(err) + } + + keyType := d.Get(keyTypeVar).(string) + req := &management.AddAppKeyRequest{ + ProjectId: d.Get(ProjectIDVar).(string), + AppId: d.Get(AppIDVar).(string), + Type: authn.KeyType(authn.KeyType_value[keyType]), + } + + if expiration, ok := d.GetOk(ExpirationDateVar); ok { + t, err := time.Parse(time.RFC3339, expiration.(string)) + if err != nil { + return diag.Errorf("failed to parse time: %v", err) + } + req.ExpirationDate = timestamppb.New(t) + } + + resp, err := client.AddAppKey(ctx, req) + if err != nil { + return diag.FromErr(err) + } + d.SetId(resp.GetId()) + if err := d.Set(KeyDetailsVar, string(resp.GetKeyDetails())); err != nil { + return diag.FromErr(err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + orgID := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, orgID) + if err != nil { + return diag.FromErr(err) + } + + projectID := d.Get(ProjectIDVar).(string) + appID := d.Get(AppIDVar).(string) + resp, err := client.GetAppKey(ctx, &management.GetAppKeyRequest{ + ProjectId: projectID, + AppId: appID, + KeyId: d.Id(), + }) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get app key") + } + d.SetId(resp.GetKey().GetId()) + + set := map[string]interface{}{ + ExpirationDateVar: resp.GetKey().GetExpirationDate().AsTime().Format(time.RFC3339), + ProjectIDVar: projectID, + AppIDVar: appID, + helper.OrgIDVar: orgID, + keyTypeVar: resp.GetKey().GetType().String(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of app key: %v", k, err) + } + } + return nil +} diff --git a/zitadel/application_key/resource.go b/zitadel/application_key/resource.go new file mode 100644 index 00000000..3a3f412f --- /dev/null +++ b/zitadel/application_key/resource.go @@ -0,0 +1,61 @@ +package application_key + +import ( + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/authn" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a app key", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + ProjectIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the project", + ForceNew: true, + }, + AppIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the application", + ForceNew: true, + }, + keyTypeVar: { + Type: schema.TypeString, + Required: true, + Description: "Type of the app key" + helper.DescriptionEnumValuesList(authn.KeyType_name), + ForceNew: true, + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(keyTypeVar, value, authn.KeyType_value) + }, + }, + ExpirationDateVar: { + Type: schema.TypeString, + Required: true, + Description: "Expiration date of the app key in the RFC3339 format", + ForceNew: true, + }, + KeyDetailsVar: { + Type: schema.TypeString, + Computed: true, + Description: "Value of the app key", + Sensitive: true, + }, + }, + DeleteContext: delete, + CreateContext: create, + ReadContext: read, + Importer: helper.ImportWithIDAndOptionalOrg( + keyIDVar, + helper.NewImportAttribute(ProjectIDVar, helper.ConvertID, false), + helper.NewImportAttribute(AppIDVar, helper.ConvertID, false), + helper.NewImportAttribute(KeyDetailsVar, helper.ConvertJSON, true), + ), + } +} diff --git a/zitadel/application_key/resource_test.go b/zitadel/application_key/resource_test.go new file mode 100644 index 00000000..ead74857 --- /dev/null +++ b/zitadel/application_key/resource_test.go @@ -0,0 +1,60 @@ +package application_key_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/application_api/application_api_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/application_key" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project/project_test_dep" +) + +func TestAccAppKey(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_application_key") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, application_key.ExpirationDateVar, exampleAttributes).AsString() + updatedProperty := "2501-01-01T08:45:00Z" + projectDep, projectID := project_test_dep.Create(t, frame) + appDep, appID := application_api_test_dep.Create(t, frame, projectID) + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, projectDep, appDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + false, + checkRemoteProperty(frame, projectID, appID), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(frame, projectID, appID), updatedProperty), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, application_key.ProjectIDVar), + test_utils.ImportStateAttribute(frame.BaseTestFrame, application_key.AppIDVar), + test_utils.ImportOrgId(frame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, application_key.KeyDetailsVar), + ), + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, projectId, appId string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetAppKey(frame, &management.GetAppKeyRequest{KeyId: frame.State(state).ID, ProjectId: projectId, AppId: appId}) + if err != nil { + return err + } + actual := remoteResource.GetKey().GetExpirationDate().AsTime().Format("2006-01-02T15:04:05Z") + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/application_oidc/const.go b/zitadel/application_oidc/const.go new file mode 100644 index 00000000..f06e41b0 --- /dev/null +++ b/zitadel/application_oidc/const.go @@ -0,0 +1,23 @@ +package application_oidc + +const ( + appIDVar = "app_id" + ProjectIDVar = "project_id" + NameVar = "name" + redirectURIsVar = "redirect_uris" + responseTypesVar = "response_types" + grantTypesVar = "grant_types" + appTypeVar = "app_type" + authMethodTypeVar = "auth_method_type" + postLogoutRedirectURIsVar = "post_logout_redirect_uris" + versionVar = "version" + devModeVar = "dev_mode" + accessTokenTypeVar = "access_token_type" + accessTokenRoleAssertionVar = "access_token_role_assertion" + idTokenRoleAssertionVar = "id_token_role_assertion" + idTokenUserinfoAssertionVar = "id_token_userinfo_assertion" + clockSkewVar = "clock_skew" + additionalOriginsVar = "additional_origins" + ClientIDVar = "client_id" + ClientSecretVar = "client_secret" +) diff --git a/zitadel/application_oidc/datasource.go b/zitadel/application_oidc/datasource.go new file mode 100644 index 00000000..00cc3fc2 --- /dev/null +++ b/zitadel/application_oidc/datasource.go @@ -0,0 +1,117 @@ +package application_oidc + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing an OIDC application belonging to a project, with all configuration possibilities.", + Schema: map[string]*schema.Schema{ + appIDVar: { + Type: schema.TypeString, + Required: true, + Description: "The ID of this resource.", + }, + helper.OrgIDVar: helper.OrgIDDatasourceField, + ProjectIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the project", + }, + NameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Name of the application", + }, + redirectURIsVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "RedirectURIs", + }, + responseTypesVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "Response type", + }, + grantTypesVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "Grant types", + }, + appTypeVar: { + Type: schema.TypeString, + Computed: true, + Description: "App type", + }, + authMethodTypeVar: { + Type: schema.TypeString, + Computed: true, + Description: "Auth method type", + }, + postLogoutRedirectURIsVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "Post logout redirect URIs", + }, + versionVar: { + Type: schema.TypeString, + Computed: true, + Description: "Version", + }, + devModeVar: { + Type: schema.TypeBool, + Computed: true, + Description: "Dev mode", + }, + accessTokenTypeVar: { + Type: schema.TypeString, + Computed: true, + Description: "Access token type", + }, + accessTokenRoleAssertionVar: { + Type: schema.TypeBool, + Computed: true, + Description: "Access token role assertion", + }, + idTokenRoleAssertionVar: { + Type: schema.TypeBool, + Computed: true, + Description: "ID token role assertion", + }, + idTokenUserinfoAssertionVar: { + Type: schema.TypeBool, + Computed: true, + Description: "Token userinfo assertion", + }, + clockSkewVar: { + Type: schema.TypeString, + Computed: true, + Description: "Clockskew", + }, + additionalOriginsVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "Additional origins", + }, + }, + ReadContext: read, + } +} diff --git a/zitadel/application_oidc/funcs.go b/zitadel/application_oidc/funcs.go new file mode 100644 index 00000000..59c95968 --- /dev/null +++ b/zitadel/application_oidc/funcs.go @@ -0,0 +1,252 @@ +package application_oidc + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/app" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveApp(ctx, &management.RemoveAppRequest{ + ProjectId: d.Get(ProjectIDVar).(string), + AppId: d.Id(), + }) + if err != nil { + return diag.Errorf("failed to delete applicationOIDC: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + projectID := d.Get(ProjectIDVar).(string) + + if d.HasChange(NameVar) { + _, err = client.UpdateApp(ctx, &management.UpdateAppRequest{ + ProjectId: projectID, + AppId: d.Id(), + Name: d.Get(NameVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update application: %v", err) + } + } + + if d.HasChanges( + redirectURIsVar, + responseTypesVar, + grantTypesVar, + appTypeVar, + authMethodTypeVar, + postLogoutRedirectURIsVar, + devModeVar, + accessTokenTypeVar, + accessTokenRoleAssertionVar, + idTokenRoleAssertionVar, + idTokenUserinfoAssertionVar, + clockSkewVar, + additionalOriginsVar, + ) { + respTypes := make([]app.OIDCResponseType, 0) + for _, respType := range d.Get(responseTypesVar).([]interface{}) { + respTypes = append(respTypes, app.OIDCResponseType(app.OIDCResponseType_value[respType.(string)])) + } + grantTypes := make([]app.OIDCGrantType, 0) + for _, grantType := range d.Get(grantTypesVar).([]interface{}) { + grantTypes = append(grantTypes, app.OIDCGrantType(app.OIDCGrantType_value[grantType.(string)])) + } + dur, err := time.ParseDuration(d.Get(clockSkewVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateOIDCAppConfig(ctx, &management.UpdateOIDCAppConfigRequest{ + ProjectId: projectID, + AppId: d.Id(), + RedirectUris: interfaceToStringSlice(d.Get(redirectURIsVar)), + ResponseTypes: respTypes, + GrantTypes: grantTypes, + AppType: app.OIDCAppType(app.OIDCAppType_value[d.Get(appTypeVar).(string)]), + AuthMethodType: app.OIDCAuthMethodType(app.OIDCAuthMethodType_value[d.Get(authMethodTypeVar).(string)]), + PostLogoutRedirectUris: interfaceToStringSlice(d.Get(postLogoutRedirectURIsVar)), + DevMode: d.Get(devModeVar).(bool), + AccessTokenType: app.OIDCTokenType(app.OIDCTokenType_value[d.Get(accessTokenTypeVar).(string)]), + AccessTokenRoleAssertion: d.Get(accessTokenRoleAssertionVar).(bool), + IdTokenRoleAssertion: d.Get(idTokenRoleAssertionVar).(bool), + IdTokenUserinfoAssertion: d.Get(idTokenUserinfoAssertionVar).(bool), + AdditionalOrigins: interfaceToStringSlice(d.Get(additionalOriginsVar)), + ClockSkew: durationpb.New(dur), + }) + if err != nil { + return diag.Errorf("failed to update applicationOIDC: %v", err) + } + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + respTypes := make([]app.OIDCResponseType, 0) + for _, respType := range d.Get(responseTypesVar).([]interface{}) { + respTypes = append(respTypes, app.OIDCResponseType(app.OIDCResponseType_value[respType.(string)])) + } + grantTypes := make([]app.OIDCGrantType, 0) + for _, grantType := range d.Get(grantTypesVar).([]interface{}) { + grantTypes = append(grantTypes, app.OIDCGrantType(app.OIDCGrantType_value[grantType.(string)])) + } + + dur, err := time.ParseDuration(d.Get(clockSkewVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.AddOIDCApp(ctx, &management.AddOIDCAppRequest{ + ProjectId: d.Get(ProjectIDVar).(string), + Name: d.Get(NameVar).(string), + RedirectUris: interfaceToStringSlice(d.Get(redirectURIsVar)), + ResponseTypes: respTypes, + GrantTypes: grantTypes, + AppType: app.OIDCAppType(app.OIDCAppType_value[(d.Get(appTypeVar).(string))]), + AuthMethodType: app.OIDCAuthMethodType(app.OIDCAuthMethodType_value[(d.Get(authMethodTypeVar).(string))]), + PostLogoutRedirectUris: interfaceToStringSlice(d.Get(postLogoutRedirectURIsVar)), + DevMode: d.Get(devModeVar).(bool), + AccessTokenType: app.OIDCTokenType(app.OIDCTokenType_value[(d.Get(accessTokenTypeVar).(string))]), + AccessTokenRoleAssertion: d.Get(accessTokenRoleAssertionVar).(bool), + IdTokenRoleAssertion: d.Get(idTokenRoleAssertionVar).(bool), + IdTokenUserinfoAssertion: d.Get(idTokenUserinfoAssertionVar).(bool), + ClockSkew: durationpb.New(dur), + AdditionalOrigins: interfaceToStringSlice(d.Get(additionalOriginsVar)), + Version: app.OIDCVersion(app.OIDCVersion_value[d.Get(versionVar).(string)]), + }) + + set := map[string]interface{}{ + ClientIDVar: resp.GetClientId(), + ClientSecretVar: resp.GetClientSecret(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of applicationOIDC: %v", k, err) + } + } + + if err != nil { + return diag.Errorf("failed to create applicationOIDC: %v", err) + } + d.SetId(resp.GetAppId()) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetAppByID(ctx, &management.GetAppByIDRequest{ProjectId: d.Get(ProjectIDVar).(string), AppId: helper.GetID(d, appIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get application oidc") + } + + oidcApp := resp.GetApp() + oidc := oidcApp.GetOidcConfig() + grantTypes := make([]string, 0) + for _, grantType := range oidc.GetGrantTypes() { + grantTypes = append(grantTypes, grantType.String()) + } + responseTypes := make([]string, 0) + for _, responseType := range oidc.GetResponseTypes() { + responseTypes = append(responseTypes, responseType.String()) + } + clockSkew := oidc.GetClockSkew().String() + if clockSkew == "" { + clockSkew = "0s" + } + + set := map[string]interface{}{ + helper.OrgIDVar: oidcApp.GetDetails().GetResourceOwner(), + NameVar: oidcApp.GetName(), + redirectURIsVar: oidc.GetRedirectUris(), + responseTypesVar: responseTypes, + grantTypesVar: grantTypes, + appTypeVar: oidc.GetAppType().String(), + authMethodTypeVar: oidc.GetAuthMethodType().String(), + postLogoutRedirectURIsVar: oidc.GetPostLogoutRedirectUris(), + versionVar: oidc.GetVersion().String(), + devModeVar: oidc.GetDevMode(), + accessTokenTypeVar: oidc.GetAccessTokenType().String(), + accessTokenRoleAssertionVar: oidc.GetAccessTokenRoleAssertion(), + idTokenRoleAssertionVar: oidc.GetIdTokenRoleAssertion(), + idTokenUserinfoAssertionVar: oidc.GetIdTokenUserinfoAssertion(), + clockSkewVar: clockSkew, + additionalOriginsVar: oidc.GetAdditionalOrigins(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of applicationOIDC: %v", k, err) + } + } + d.SetId(oidcApp.GetId()) + return nil +} + +func interfaceToStringSlice(in interface{}) []string { + slice := in.([]interface{}) + ret := make([]string, 0) + for _, item := range slice { + ret = append(ret, item.(string)) + } + return ret +} diff --git a/zitadel/application_oidc/resource.go b/zitadel/application_oidc/resource.go new file mode 100644 index 00000000..d83f4603 --- /dev/null +++ b/zitadel/application_oidc/resource.go @@ -0,0 +1,160 @@ +package application_oidc + +import ( + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/app" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing an OIDC application belonging to a project, with all configuration possibilities.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + ProjectIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the project", + ForceNew: true, + }, + NameVar: { + Type: schema.TypeString, + Required: true, + Description: "Name of the application", + }, + redirectURIsVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + Description: "RedirectURIs", + }, + responseTypesVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + Description: "Response type" + helper.DescriptionEnumValuesList(app.OIDCResponseType_name), + /* Not yet supported + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return enumValuesValidation(applicationAuthMethodTypeVar, value, app.OIDCResponseType_value) + },*/ + }, + grantTypesVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + Description: "Grant types" + helper.DescriptionEnumValuesList(app.OIDCGrantType_name), + /* Not yet supported + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return enumValuesValidation(applicationGrantTypesVar, value, app.OIDCGrantType_value) + },*/ + }, + appTypeVar: { + Type: schema.TypeString, + Optional: true, + Description: "App type" + helper.DescriptionEnumValuesList(app.OIDCAppType_name), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(appTypeVar, value, app.OIDCAppType_value) + }, + Default: app.OIDCAppType_name[0], + }, + authMethodTypeVar: { + Type: schema.TypeString, + Optional: true, + Description: "Auth method type" + helper.DescriptionEnumValuesList(app.OIDCAuthMethodType_name), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(authMethodTypeVar, value, app.OIDCAuthMethodType_value) + }, + Default: app.OIDCAuthMethodType_name[0], + }, + postLogoutRedirectURIsVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "Post logout redirect URIs", + }, + versionVar: { + Type: schema.TypeString, + Optional: true, + Description: "Version" + helper.DescriptionEnumValuesList(app.OIDCVersion_name), + Default: app.OIDCVersion_name[0], + ForceNew: true, + }, + devModeVar: { + Type: schema.TypeBool, + Optional: true, + Description: "Dev mode", + }, + accessTokenTypeVar: { + Type: schema.TypeString, + Optional: true, + Description: "Access token type" + helper.DescriptionEnumValuesList(app.OIDCTokenType_name), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(accessTokenTypeVar, value, app.OIDCTokenType_value) + }, + Default: app.OIDCTokenType_name[0], + }, + accessTokenRoleAssertionVar: { + Type: schema.TypeBool, + Optional: true, + Description: "Access token role assertion", + }, + idTokenRoleAssertionVar: { + Type: schema.TypeBool, + Optional: true, + Description: "ID token role assertion", + }, + idTokenUserinfoAssertionVar: { + Type: schema.TypeBool, + Optional: true, + Description: "Token userinfo assertion", + }, + clockSkewVar: { + Type: schema.TypeString, + Optional: true, + Description: "Clockskew", + Default: "0s", + }, + additionalOriginsVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "Additional origins", + }, + ClientIDVar: { + Type: schema.TypeString, + Computed: true, + Description: "generated ID for this config", + Sensitive: true, + }, + ClientSecretVar: { + Type: schema.TypeString, + Computed: true, + Description: "generated secret for this config", + Sensitive: true, + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithIDAndOptionalOrg( + appIDVar, + helper.NewImportAttribute(ProjectIDVar, helper.ConvertID, false), + helper.NewImportAttribute(ClientIDVar, helper.ConvertNonEmpty, true), + helper.NewImportAttribute(ClientSecretVar, helper.ConvertNonEmpty, true), + ), + } +} diff --git a/zitadel/application_oidc/resource_test.go b/zitadel/application_oidc/resource_test.go new file mode 100644 index 00000000..6b02d07b --- /dev/null +++ b/zitadel/application_oidc/resource_test.go @@ -0,0 +1,57 @@ +package application_oidc_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/application_oidc" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project/project_test_dep" +) + +func TestAccAppOIDC(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_application_oidc") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, application_oidc.NameVar, exampleAttributes).AsString() + projectDep, projectID := project_test_dep.Create(t, frame) + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, projectDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedproperty", + "", "", "", + false, + checkRemoteProperty(frame, projectID), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(frame, projectID), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, application_oidc.ProjectIDVar), + test_utils.ImportOrgId(frame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, application_oidc.ClientIDVar), + test_utils.ImportStateAttribute(frame.BaseTestFrame, application_oidc.ClientSecretVar), + ), + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, projectId string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetAppByID(frame, &management.GetAppByIDRequest{AppId: frame.State(state).ID, ProjectId: projectId}) + if err != nil { + return err + } + actual := remoteResource.GetApp().GetName() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_domain_claimed_message_text/resource.go b/zitadel/default_domain_claimed_message_text/resource.go new file mode 100644 index 00000000..ff51fee5 --- /dev/null +++ b/zitadel/default_domain_claimed_message_text/resource.go @@ -0,0 +1,237 @@ +package default_domain_claimed_message_text + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &defaultDomainClaimedMessageTextResource{} +) + +func New() resource.Resource { + return &defaultDomainClaimedMessageTextResource{} +} + +type defaultDomainClaimedMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *defaultDomainClaimedMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_default_domain_claimed_message_text" +} + +func (r *defaultDomainClaimedMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + s, d := text.GenSchemaMessageCustomText(ctx) + delete(s.Attributes, "org_id") + return s, d +} + +func (r *defaultDomainClaimedMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *defaultDomainClaimedMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultDomainClaimedMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultDomainClaimedMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultDomainClaimedMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + language := getID(ctx, state) + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomDomainClaimedMessageText(ctx, &admin.GetCustomDomainClaimedMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *defaultDomainClaimedMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultDomainClaimedMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultDomainClaimedMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultDomainClaimedMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomDomainClaimedMessageTextToDefault(ctx, &admin.ResetCustomDomainClaimedMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(language) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) string { + return helper.GetStringFromAttr(ctx, obj.Attributes(), "id") +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) string { + + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + + return language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) string { + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + + return language +} diff --git a/zitadel/default_domain_claimed_message_text/resource_test.go b/zitadel/default_domain_claimed_message_text/resource_test.go new file mode 100644 index 00000000..9c5028a2 --- /dev/null +++ b/zitadel/default_domain_claimed_message_text/resource_test.go @@ -0,0 +1,51 @@ +package default_domain_claimed_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_domain_claimed_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultDomainClaimedMessageText(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_domain_claimed_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + exampleLanguage := test_utils.AttributeValue(t, default_domain_claimed_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtitle", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("ZITADEL - Domain has been claimed"), + nil, // Import not supported + ) +} + +func checkRemoteProperty(frame *test_utils.InstanceTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomDomainClaimedMessageText(frame, &admin.GetCustomDomainClaimedMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_domain_policy/const.go b/zitadel/default_domain_policy/const.go new file mode 100644 index 00000000..03a31e7f --- /dev/null +++ b/zitadel/default_domain_policy/const.go @@ -0,0 +1,7 @@ +package default_domain_policy + +const ( + UserLoginMustBeDomainVar = "user_login_must_be_domain" + validateOrgDomainVar = "validate_org_domains" + smtpSenderVar = "smtp_sender_address_matches_instance_domain" +) diff --git a/zitadel/default_domain_policy/funcs.go b/zitadel/default_domain_policy/funcs.go new file mode 100644 index 00000000..512f0158 --- /dev/null +++ b/zitadel/default_domain_policy/funcs.go @@ -0,0 +1,92 @@ +package default_domain_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "default domain policy cannot be deleted") + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + id := "" + if d.HasChanges(UserLoginMustBeDomainVar, validateOrgDomainVar, smtpSenderVar) { + resp, err := client.UpdateDomainPolicy(ctx, &admin.UpdateDomainPolicyRequest{ + UserLoginMustBeDomain: d.Get(UserLoginMustBeDomainVar).(bool), + ValidateOrgDomains: d.Get(validateOrgDomainVar).(bool), + SmtpSenderAddressMatchesInstanceDomain: d.Get(smtpSenderVar).(bool), + }) + if helper.IgnorePreconditionError(err) != nil { + return diag.Errorf("failed to update default domain policy: %v", err) + } + if resp != nil { + id = resp.GetDetails().GetResourceOwner() + } + } + if id == "" { + resp, err := client.GetDomainPolicy(ctx, &admin.GetDomainPolicyRequest{}) + if err != nil { + return diag.Errorf("failed to update default domain policy: %v", err) + } + id = resp.GetPolicy().GetDetails().GetResourceOwner() + } + d.SetId(id) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetDomainPolicy(ctx, &admin.GetDomainPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get default domain policy") + } + policy := resp.Policy + set := map[string]interface{}{ + UserLoginMustBeDomainVar: policy.GetUserLoginMustBeDomain(), + validateOrgDomainVar: policy.GetValidateOrgDomains(), + smtpSenderVar: policy.GetSmtpSenderAddressMatchesInstanceDomain(), + } + + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of default domain policy: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/default_domain_policy/resource.go b/zitadel/default_domain_policy/resource.go new file mode 100644 index 00000000..065be12c --- /dev/null +++ b/zitadel/default_domain_policy/resource.go @@ -0,0 +1,35 @@ +package default_domain_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the default domain policy.", + Schema: map[string]*schema.Schema{ + UserLoginMustBeDomainVar: { + Type: schema.TypeBool, + Required: true, + Description: "User login must be domain", + }, + validateOrgDomainVar: { + Type: schema.TypeBool, + Required: true, + Description: "Validate organization domains", + }, + smtpSenderVar: { + Type: schema.TypeBool, + Required: true, + Description: "", + }, + }, + ReadContext: read, + CreateContext: update, + DeleteContext: delete, + UpdateContext: update, + Importer: helper.ImportWithEmptyID(), + } +} diff --git a/zitadel/default_domain_policy/resource_test.go b/zitadel/default_domain_policy/resource_test.go new file mode 100644 index 00000000..4dc91761 --- /dev/null +++ b/zitadel/default_domain_policy/resource_test.go @@ -0,0 +1,54 @@ +package default_domain_policy_test + +import ( + "fmt" + "strconv" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_domain_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultDomainPolicy(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_domain_policy") + resourceExample, resourceAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, default_domain_policy.UserLoginMustBeDomainVar, resourceAttributes).True() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + func(property bool, secret string) string { + // only replace first bool for the smtp_sender_address_matches_instance_domain property + return strings.Replace(resourceExample, strconv.FormatBool(exampleProperty), strconv.FormatBool(property), 1) + }, + exampleProperty, !exampleProperty, + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckNothing, + test_utils.ImportNothing, + ) +} + +func checkRemoteProperty(frame test_utils.InstanceTestFrame) func(bool) resource.TestCheckFunc { + return func(expect bool) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetDomainPolicy(frame, &admin.GetDomainPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetUserLoginMustBeDomain() + if actual != expect { + return fmt.Errorf("expected %t, but got %t", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_init_message_text/resource.go b/zitadel/default_init_message_text/resource.go new file mode 100644 index 00000000..e5868165 --- /dev/null +++ b/zitadel/default_init_message_text/resource.go @@ -0,0 +1,234 @@ +package default_init_message_text + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &defaultInitMessageTextResource{} +) + +func New() resource.Resource { + return &defaultInitMessageTextResource{} +} + +type defaultInitMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *defaultInitMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_default_init_message_text" +} + +func (r *defaultInitMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + s, d := text.GenSchemaMessageCustomText(ctx) + delete(s.Attributes, "org_id") + return s, d +} + +func (r *defaultInitMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *defaultInitMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultInitMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultInitMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultInitMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + language := getID(ctx, state) + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomInitMessageText(ctx, &admin.GetCustomInitMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *defaultInitMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultInitMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultInitMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultInitMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomInitMessageTextToDefault(ctx, &admin.ResetCustomInitMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(language) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) string { + return helper.GetStringFromAttr(ctx, obj.Attributes(), "id") +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) string { + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) string { + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} diff --git a/zitadel/default_init_message_text/resource_test.go b/zitadel/default_init_message_text/resource_test.go new file mode 100644 index 00000000..e950c7bd --- /dev/null +++ b/zitadel/default_init_message_text/resource_test.go @@ -0,0 +1,51 @@ +package default_init_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_init_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultInitMessageText(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_init_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + language := test_utils.AttributeValue(t, default_init_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtitle", + "", "", "", + true, + checkRemoteProperty(frame, language), + regexp.MustCompile(fmt.Sprintf(`^%s$`, language)), + // When deleted, the default should be returned + checkRemoteProperty(frame, language)("ZITADEL - Initialize User"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.InstanceTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomInitMessageText(frame, &admin.GetCustomInitMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_label_policy/const.go b/zitadel/default_label_policy/const.go new file mode 100644 index 00000000..6b9d8b1d --- /dev/null +++ b/zitadel/default_label_policy/const.go @@ -0,0 +1,40 @@ +package default_label_policy + +const ( + PrimaryColorVar = "primary_color" + hideLoginNameSuffixVar = "hide_login_name_suffix" + warnColorVar = "warn_color" + backgroundColorVar = "background_color" + fontColorVar = "font_color" + primaryColorDarkVar = "primary_color_dark" + backgroundColorDarkVar = "background_color_dark" + warnColorDarkVar = "warn_color_dark" + fontColorDarkVar = "font_color_dark" + disableWatermarkVar = "disable_watermark" + LogoPathVar = "logo_path" + LogoHashVar = "logo_hash" + logoURLVar = "logo_url" + IconPathVar = "icon_path" + IconHashVar = "icon_hash" + iconURLVar = "icon_url" + LogoDarkPathVar = "logo_dark_path" + LogoDarkHashVar = "logo_dark_hash" + logoURLDarkVar = "logo_url_dark" + IconDarkPathVar = "icon_dark_path" + IconDarkHashVar = "icon_dark_hash" + iconURLDarkVar = "icon_url_dark" + FontPathVar = "font_path" + FontHashVar = "font_hash" + fontURLVar = "font_url" + SetActiveVar = "set_active" +) + +const ( + assetAPI = "/assets/v1" + labelPolicyURL = "/instance/policy/label" + logoURL = assetAPI + labelPolicyURL + "/logo" + logoDarkURL = logoURL + "/dark" + iconURL = assetAPI + labelPolicyURL + "/icon" + iconDarkURL = iconURL + "/dark" + fontURL = assetAPI + labelPolicyURL + "/font" +) diff --git a/zitadel/default_label_policy/funcs.go b/zitadel/default_label_policy/funcs.go new file mode 100644 index 00000000..9256e1a6 --- /dev/null +++ b/zitadel/default_label_policy/funcs.go @@ -0,0 +1,173 @@ +package default_label_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "default label policy cannot be deleted") + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + id := "" + if d.HasChanges( + PrimaryColorVar, + hideLoginNameSuffixVar, + warnColorVar, + backgroundColorVar, + fontColorVar, + primaryColorDarkVar, + backgroundColorDarkVar, + warnColorDarkVar, + fontColorDarkVar, + disableWatermarkVar, + ) { + resp, err := client.UpdateLabelPolicy(ctx, &admin.UpdateLabelPolicyRequest{ + PrimaryColor: d.Get(PrimaryColorVar).(string), + HideLoginNameSuffix: d.Get(hideLoginNameSuffixVar).(bool), + WarnColor: d.Get(warnColorVar).(string), + BackgroundColor: d.Get(backgroundColorVar).(string), + FontColor: d.Get(fontColorVar).(string), + PrimaryColorDark: d.Get(primaryColorDarkVar).(string), + BackgroundColorDark: d.Get(backgroundColorDarkVar).(string), + WarnColorDark: d.Get(warnColorDarkVar).(string), + FontColorDark: d.Get(fontColorDarkVar).(string), + DisableWatermark: d.Get(disableWatermarkVar).(bool), + }) + if helper.IgnorePreconditionError(err) != nil { + return diag.Errorf("failed to update default label policy: %v", err) + } + if resp != nil { + id = resp.Details.ResourceOwner + } + } + if id == "" { + resp, err := client.GetLabelPolicy(ctx, &admin.GetLabelPolicyRequest{}) + if err != nil { + return diag.Errorf("failed to update default label policy: %v", err) + } + id = resp.GetPolicy().GetDetails().GetResourceOwner() + } + d.SetId(id) + + if d.HasChanges(LogoHashVar, LogoPathVar) { + if err := helper.InstanceFormFilePost(clientinfo, logoURL, d.Get(LogoPathVar).(string)); err != nil { + return diag.Errorf("failed to upload logo: %v", err) + } + } + if d.HasChanges(LogoDarkHashVar, LogoDarkPathVar) { + if err := helper.InstanceFormFilePost(clientinfo, logoDarkURL, d.Get(LogoDarkPathVar).(string)); err != nil { + return diag.Errorf("failed to upload logo dark: %v", err) + } + } + if d.HasChanges(IconHashVar, IconPathVar) { + if err := helper.InstanceFormFilePost(clientinfo, iconURL, d.Get(IconPathVar).(string)); err != nil { + return diag.Errorf("failed to upload icon: %v", err) + } + } + if d.HasChanges(IconDarkHashVar, IconDarkPathVar) { + if err := helper.InstanceFormFilePost(clientinfo, iconDarkURL, d.Get(IconDarkPathVar).(string)); err != nil { + return diag.Errorf("failed to upload icon dark: %v", err) + } + } + if d.HasChanges(FontHashVar, FontPathVar) { + if err := helper.InstanceFormFilePost(clientinfo, fontURL, d.Get(FontPathVar).(string)); err != nil { + return diag.Errorf("failed to upload font: %v", err) + } + } + + if d.HasChanges( + PrimaryColorVar, + hideLoginNameSuffixVar, + warnColorVar, + backgroundColorVar, + fontColorVar, + primaryColorDarkVar, + backgroundColorDarkVar, + warnColorDarkVar, + fontColorDarkVar, + disableWatermarkVar, + LogoHashVar, + LogoDarkHashVar, + IconHashVar, + IconDarkHashVar, + FontHashVar, + ) { + if d.Get(SetActiveVar).(bool) { + if _, err := client.ActivateLabelPolicy(ctx, &admin.ActivateLabelPolicyRequest{}); err != nil { + return diag.Errorf("failed to activate default label policy: %v", err) + } + } + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetLabelPolicy(ctx, &admin.GetLabelPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get default label policy") + } + + policy := resp.Policy + set := map[string]interface{}{ + PrimaryColorVar: policy.GetPrimaryColor(), + hideLoginNameSuffixVar: policy.GetHideLoginNameSuffix(), + warnColorVar: policy.GetWarnColor(), + backgroundColorVar: policy.GetBackgroundColor(), + fontColorVar: policy.GetFontColor(), + primaryColorDarkVar: policy.GetPrimaryColorDark(), + backgroundColorDarkVar: policy.GetBackgroundColorDark(), + warnColorDarkVar: policy.GetWarnColorDark(), + fontColorDarkVar: policy.GetFontColorDark(), + disableWatermarkVar: policy.GetDisableWatermark(), + logoURLVar: policy.GetLogoUrl(), + iconURLVar: policy.GetIconUrl(), + logoURLDarkVar: policy.GetLogoUrlDark(), + iconURLDarkVar: policy.GetIconUrlDark(), + fontURLVar: policy.GetFontUrl(), + } + + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of default label policy: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/default_label_policy/resource.go b/zitadel/default_label_policy/resource.go new file mode 100644 index 00000000..6a8f3f68 --- /dev/null +++ b/zitadel/default_label_policy/resource.go @@ -0,0 +1,150 @@ +package default_label_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the default label policy.", + Schema: map[string]*schema.Schema{ + PrimaryColorVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for primary color", + }, + hideLoginNameSuffixVar: { + Type: schema.TypeBool, + Required: true, + Description: "hides the org suffix on the login form if the scope \"urn:zitadel:iam:org:domain:primary:{domainname}\" is set. Details about this scope in https://docs.zitadel.ch/concepts#Reserved_Scopes", + }, + warnColorVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for warn color", + }, + backgroundColorVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for background color", + }, + fontColorVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for font color", + }, + primaryColorDarkVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for primary color dark theme", + }, + backgroundColorDarkVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for background color dark theme", + }, + warnColorDarkVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for warn color dark theme", + }, + fontColorDarkVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for font color dark theme", + }, + disableWatermarkVar: { + Type: schema.TypeBool, + Required: true, + Description: "disable watermark", + }, + LogoPathVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + LogoHashVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + logoURLVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + IconPathVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + IconHashVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + iconURLVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + LogoDarkPathVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + LogoDarkHashVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + logoURLDarkVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + IconDarkPathVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + IconDarkHashVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + iconURLDarkVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + FontPathVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + FontHashVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + fontURLVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + SetActiveVar: { + Type: schema.TypeBool, + Optional: true, + Description: "set the label policy active after creating/updating", + }, + }, + ReadContext: read, + CreateContext: update, + DeleteContext: delete, + UpdateContext: update, + Importer: helper.ImportWithEmptyID(), + } +} diff --git a/zitadel/default_label_policy/resource_test.go b/zitadel/default_label_policy/resource_test.go new file mode 100644 index 00000000..6d3c97eb --- /dev/null +++ b/zitadel/default_label_policy/resource_test.go @@ -0,0 +1,102 @@ +package default_label_policy_test + +import ( + "encoding/base64" + "fmt" + "io" + "os" + "regexp" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_label_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultLabelPolicy(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_label_policy") + testSVGFile := writeFile(t, strings.NewReader(testSVG)) + defer testSVGFile.Close() + defer os.Remove(testSVGFile.Name()) + testFontFile := writeFile(t, base64.NewDecoder(base64.StdEncoding, strings.NewReader(testFontBase64))) + defer testFontFile.Close() + defer os.Remove(testFontFile.Name()) + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + resourceExample = regexp.MustCompile("/path/to/[a-zA-Z_]+\\.jpg").ReplaceAllString(resourceExample, testSVGFile.Name()) + resourceExample = regexp.MustCompile("/path/to/[a-zA-Z_]+\\.tff").ReplaceAllString(resourceExample, testFontFile.Name()) + exampleProperty := test_utils.AttributeValue(t, default_label_policy.PrimaryColorVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "#5469d3", + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckNothing, + test_utils.ImportNothing, + default_label_policy.SetActiveVar, + default_label_policy.LogoHashVar, + default_label_policy.LogoPathVar, + default_label_policy.LogoDarkHashVar, + default_label_policy.LogoDarkPathVar, + default_label_policy.IconHashVar, + default_label_policy.IconPathVar, + default_label_policy.IconDarkHashVar, + default_label_policy.IconDarkPathVar, + default_label_policy.FontHashVar, + default_label_policy.FontPathVar, + ) +} + +func checkRemoteProperty(frame test_utils.InstanceTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetLabelPolicy(frame, &admin.GetLabelPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetPrimaryColor() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} + +func writeFile(t *testing.T, content io.Reader) *os.File { + file, err := os.CreateTemp("", "TestAccDefaultLabelPolicy") + if err != nil { + t.Fatalf("creating temp file failed: %v", err) + } + if _, err := io.Copy(file, content); err != nil { + t.Fatalf("writing temp file failed: %v", err) + } + return file +} + +const ( + testSVG = ` + + + +` + testFontBase64 = ` +AAEAAAAHAEAAAgAwY21hcAAJAHYAAAEAAAAALGdseWbxy2aYAAABNAAAAFxoZWFk8jXd+AAAAHwA +AAA2aGhlYQZhAMoAAAC0AAAAJGhtdHgEdABqAAAA+AAAAAhsb2NhAC4AFAAAASwAAAAGbWF4cAAF +AAsAAADYAAAAIAABAAAAAQAA9ZwpRF8PPPUAAgPoAAAAALSS9AAAAAAA3C+mXAAGAAACWAK8AAAA +AwACAAAAAAAAAAEAAAQA/nAAAAJYAAb//wJYAAEAAAAAAAAAAAAAAAAAAAACAAEAAAACAAsAAgAA +AAAAAAAAAAAAAAAAAAAAAAAAAAACWABkAhwABgAAAAEAAAADAAAADAAEACAAAAAEAAQAAQAAAEH/ +/wAAAEH////AAAEAAAAAAAAAFAAuAAAAAgBkAAACWAK8AAMABwAAMxEhESUhESFkAfT+NAGk/lwC +vP1EKAJsAAIABgAAAh0CkAACAAoAABMzAwETMxMjJyMHrcRj/vjaYN1ZPu9CAQsBQP21ApD9cMjI +AA== +` +) diff --git a/zitadel/default_lockout_policy/const.go b/zitadel/default_lockout_policy/const.go new file mode 100644 index 00000000..3be06c27 --- /dev/null +++ b/zitadel/default_lockout_policy/const.go @@ -0,0 +1,5 @@ +package default_lockout_policy + +const ( + MaxPasswordAttemptsVar = "max_password_attempts" +) diff --git a/zitadel/default_lockout_policy/funcs.go b/zitadel/default_lockout_policy/funcs.go new file mode 100644 index 00000000..739f4f57 --- /dev/null +++ b/zitadel/default_lockout_policy/funcs.go @@ -0,0 +1,89 @@ +package default_lockout_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "default lockout policy cannot be deleted") + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + id := "" + if d.HasChanges(MaxPasswordAttemptsVar) { + resp, err := client.UpdateLockoutPolicy(ctx, &admin.UpdateLockoutPolicyRequest{ + MaxPasswordAttempts: uint32(d.Get(MaxPasswordAttemptsVar).(int)), + }) + if helper.IgnorePreconditionError(err) != nil { + return diag.Errorf("failed to update default lockout policy: %v", err) + } + if resp != nil { + id = resp.GetDetails().GetResourceOwner() + } + } + if id == "" { + resp, err := client.GetLockoutPolicy(ctx, &admin.GetLockoutPolicyRequest{}) + if err != nil { + return diag.Errorf("failed to update default lockout policy: %v", err) + } + id = resp.GetPolicy().GetDetails().GetResourceOwner() + } + d.SetId(id) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetLockoutPolicy(ctx, &admin.GetLockoutPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get default lockout policy") + } + + policy := resp.Policy + set := map[string]interface{}{ + MaxPasswordAttemptsVar: policy.GetMaxPasswordAttempts(), + } + + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of default lockout policy: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/default_lockout_policy/resource.go b/zitadel/default_lockout_policy/resource.go new file mode 100644 index 00000000..e86e03cf --- /dev/null +++ b/zitadel/default_lockout_policy/resource.go @@ -0,0 +1,25 @@ +package default_lockout_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the default lockout policy.", + Schema: map[string]*schema.Schema{ + MaxPasswordAttemptsVar: { + Type: schema.TypeInt, + Required: true, + Description: "Maximum password check attempts before the account gets locked. Attempts are reset as soon as the password is entered correctly or the password is reset.", + }, + }, + DeleteContext: delete, + CreateContext: update, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithEmptyID(), + } +} diff --git a/zitadel/default_lockout_policy/resource_test.go b/zitadel/default_lockout_policy/resource_test.go new file mode 100644 index 00000000..c3c5047a --- /dev/null +++ b/zitadel/default_lockout_policy/resource_test.go @@ -0,0 +1,53 @@ +package default_lockout_policy_test + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_lockout_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultLockoutPolicy(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_lockout_policy") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty, err := strconv.ParseUint(test_utils.AttributeValue(t, default_lockout_policy.MaxPasswordAttemptsVar, exampleAttributes).AsString(), 10, 64) + if err != nil { + t.Fatalf("could not parse example property: %v", err) + } + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, 10, + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckNothing, + test_utils.ImportNothing, + ) +} + +func checkRemoteProperty(frame test_utils.InstanceTestFrame) func(uint64) resource.TestCheckFunc { + return func(expect uint64) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetLockoutPolicy(frame, &admin.GetLockoutPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetMaxPasswordAttempts() + if actual != expect { + return fmt.Errorf("expected %d, but got %d", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_login_policy/const.go b/zitadel/default_login_policy/const.go new file mode 100644 index 00000000..dac49b86 --- /dev/null +++ b/zitadel/default_login_policy/const.go @@ -0,0 +1,24 @@ +package default_login_policy + +const ( + allowUsernamePasswordVar = "user_login" + allowRegisterVar = "allow_register" + allowExternalIDPVar = "allow_external_idp" + forceMFAVar = "force_mfa" + forceMFALocalOnlyVar = "force_mfa_local_only" + passwordlessTypeVar = "passwordless_type" + hidePasswordResetVar = "hide_password_reset" + passwordCheckLifetimeVar = "password_check_lifetime" + externalLoginCheckLifetimeVar = "external_login_check_lifetime" + mfaInitSkipLifetimeVar = "mfa_init_skip_lifetime" + secondFactorCheckLifetimeVar = "second_factor_check_lifetime" + multiFactorCheckLifetimeVar = "multi_factor_check_lifetime" + ignoreUnknownUsernamesVar = "ignore_unknown_usernames" + DefaultRedirectURIVar = "default_redirect_uri" + secondFactorsVar = "second_factors" + multiFactorsVar = "multi_factors" + idpsVar = "idps" + allowDomainDiscovery = "allow_domain_discovery" + disableLoginWithEmail = "disable_login_with_email" + disableLoginWithPhone = "disable_login_with_phone" +) diff --git a/zitadel/default_login_policy/funcs.go b/zitadel/default_login_policy/funcs.go new file mode 100644 index 00000000..1a4eb9e2 --- /dev/null +++ b/zitadel/default_login_policy/funcs.go @@ -0,0 +1,275 @@ +package default_login_policy + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/policy" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "default login policy cannot be deleted") + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + id := "" + if d.HasChanges(passwordCheckLifetimeVar, + externalLoginCheckLifetimeVar, + mfaInitSkipLifetimeVar, + secondFactorCheckLifetimeVar, + multiFactorCheckLifetimeVar, + allowUsernamePasswordVar, + allowRegisterVar, + allowExternalIDPVar, + forceMFAVar, + forceMFALocalOnlyVar, + passwordlessTypeVar, + hidePasswordResetVar, + ignoreUnknownUsernamesVar, + DefaultRedirectURIVar, + allowDomainDiscovery, + disableLoginWithEmail, + disableLoginWithPhone, + ) { + passwordCheckLT, err := time.ParseDuration(d.Get(passwordCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + externalLoginCheckLT, err := time.ParseDuration(d.Get(externalLoginCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + mfaInitSkipLT, err := time.ParseDuration(d.Get(mfaInitSkipLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + secondFactorCheckLT, err := time.ParseDuration(d.Get(secondFactorCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + multiFactorCheckLT, err := time.ParseDuration(d.Get(multiFactorCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.UpdateLoginPolicy(ctx, &admin.UpdateLoginPolicyRequest{ + AllowUsernamePassword: d.Get(allowUsernamePasswordVar).(bool), + AllowRegister: d.Get(allowRegisterVar).(bool), + AllowExternalIdp: d.Get(allowExternalIDPVar).(bool), + ForceMfa: d.Get(forceMFAVar).(bool), + PasswordlessType: policy.PasswordlessType(policy.PasswordlessType_value[d.Get(passwordlessTypeVar).(string)]), + HidePasswordReset: d.Get(hidePasswordResetVar).(bool), + IgnoreUnknownUsernames: d.Get(ignoreUnknownUsernamesVar).(bool), + DefaultRedirectUri: d.Get(DefaultRedirectURIVar).(string), + PasswordCheckLifetime: durationpb.New(passwordCheckLT), + ExternalLoginCheckLifetime: durationpb.New(externalLoginCheckLT), + MfaInitSkipLifetime: durationpb.New(mfaInitSkipLT), + SecondFactorCheckLifetime: durationpb.New(secondFactorCheckLT), + MultiFactorCheckLifetime: durationpb.New(multiFactorCheckLT), + AllowDomainDiscovery: d.Get(allowDomainDiscovery).(bool), + DisableLoginWithEmail: d.Get(disableLoginWithEmail).(bool), + DisableLoginWithPhone: d.Get(disableLoginWithPhone).(bool), + ForceMfaLocalOnly: d.Get(forceMFALocalOnlyVar).(bool), + }) + if helper.IgnorePreconditionError(err) != nil { + return diag.Errorf("failed to update login policy: %v", err) + } + if resp != nil { + id = resp.GetDetails().GetResourceOwner() + } + } + + if id == "" { + resp, err := client.GetLoginPolicy(ctx, &admin.GetLoginPolicyRequest{}) + if err != nil { + return diag.Errorf("failed to update default login policy: %v", err) + } + id = resp.GetPolicy().GetDetails().GetResourceOwner() + } + d.SetId(id) + + if d.HasChange(secondFactorsVar) { + o, err := client.ListLoginPolicySecondFactors(ctx, &admin.ListLoginPolicySecondFactorsRequest{}) + if err != nil { + return diag.Errorf("failed to get default login policy second factors: %v", err) + } + factors := make([]string, len(o.GetResult())) + for i, factor := range o.GetResult() { + factors[i] = policy.SecondFactorType_name[int32(factor.Number())] + } + addSecondFactor, deleteSecondFactors := helper.GetAddAndDelete(factors, helper.SetToStringSlice(d.Get(secondFactorsVar).(*schema.Set))) + + for _, factor := range addSecondFactor { + if _, err := client.AddSecondFactorToLoginPolicy(ctx, &admin.AddSecondFactorToLoginPolicyRequest{ + Type: policy.SecondFactorType(policy.SecondFactorType_value[factor]), + }); helper.IgnoreAlreadyExistsError(err) != nil { + return diag.FromErr(err) + } + } + for _, factor := range deleteSecondFactors { + if _, err := client.RemoveSecondFactorFromLoginPolicy(ctx, &admin.RemoveSecondFactorFromLoginPolicyRequest{ + Type: policy.SecondFactorType(policy.SecondFactorType_value[factor]), + }); helper.IgnoreAlreadyExistsError(err) != nil { + return diag.FromErr(err) + } + } + } + + if d.HasChange(multiFactorsVar) { + o, err := client.ListLoginPolicyMultiFactors(ctx, &admin.ListLoginPolicyMultiFactorsRequest{}) + if err != nil { + return diag.Errorf("failed to get default login policy multi factors: %v", err) + } + factors := make([]string, len(o.GetResult())) + for i, factor := range o.GetResult() { + factors[i] = policy.MultiFactorType_name[int32(factor.Number())] + } + addMultiFactor, deleteMultiFactors := helper.GetAddAndDelete(factors, helper.SetToStringSlice(d.Get(multiFactorsVar).(*schema.Set))) + + for _, factor := range addMultiFactor { + if _, err := client.AddMultiFactorToLoginPolicy(ctx, &admin.AddMultiFactorToLoginPolicyRequest{ + Type: policy.MultiFactorType(policy.MultiFactorType_value[factor]), + }); err != nil { + return diag.FromErr(err) + } + } + for _, factor := range deleteMultiFactors { + if _, err := client.RemoveMultiFactorFromLoginPolicy(ctx, &admin.RemoveMultiFactorFromLoginPolicyRequest{ + Type: policy.MultiFactorType(policy.MultiFactorType_value[factor]), + }); err != nil { + return diag.FromErr(err) + } + } + } + + if d.HasChange(idpsVar) { + o, err := client.ListLoginPolicyIDPs(ctx, &admin.ListLoginPolicyIDPsRequest{}) + if err != nil { + return diag.Errorf("failed to get default login policy idps: %v", err) + } + + idps := make([]string, len(o.GetResult())) + for i, idp := range o.GetResult() { + idps[i] = idp.IdpId + } + addIdps, deleteIdps := helper.GetAddAndDelete(idps, helper.SetToStringSlice(d.Get(idpsVar).(*schema.Set))) + + for _, addIdp := range addIdps { + if _, err := client.AddIDPToLoginPolicy(ctx, &admin.AddIDPToLoginPolicyRequest{IdpId: addIdp}); err != nil { + return diag.FromErr(err) + } + } + for _, deleteIdp := range deleteIdps { + if _, err := client.RemoveIDPFromLoginPolicy(ctx, &admin.RemoveIDPFromLoginPolicyRequest{IdpId: deleteIdp}); err != nil { + return diag.FromErr(err) + } + } + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetLoginPolicy(ctx, &admin.GetLoginPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get default login policy") + } + + set := map[string]interface{}{ + allowUsernamePasswordVar: resp.Policy.GetAllowUsernamePassword(), + allowRegisterVar: resp.Policy.GetAllowRegister(), + allowExternalIDPVar: resp.Policy.GetAllowExternalIdp(), + forceMFAVar: resp.Policy.GetForceMfa(), + passwordlessTypeVar: resp.Policy.GetPasswordlessType().String(), + hidePasswordResetVar: resp.Policy.GetHidePasswordReset(), + ignoreUnknownUsernamesVar: resp.Policy.GetIgnoreUnknownUsernames(), + DefaultRedirectURIVar: resp.Policy.GetDefaultRedirectUri(), + passwordCheckLifetimeVar: resp.Policy.GetPasswordCheckLifetime().AsDuration().String(), + externalLoginCheckLifetimeVar: resp.Policy.GetExternalLoginCheckLifetime().AsDuration().String(), + mfaInitSkipLifetimeVar: resp.Policy.GetMfaInitSkipLifetime().AsDuration().String(), + secondFactorCheckLifetimeVar: resp.Policy.GetSecondFactorCheckLifetime().AsDuration().String(), + multiFactorCheckLifetimeVar: resp.Policy.GetMultiFactorCheckLifetime().AsDuration().String(), + allowDomainDiscovery: resp.Policy.GetAllowDomainDiscovery(), + disableLoginWithEmail: resp.Policy.GetDisableLoginWithEmail(), + disableLoginWithPhone: resp.Policy.GetDisableLoginWithPhone(), + forceMFALocalOnlyVar: resp.Policy.GetForceMfaLocalOnly(), + } + + respSecond, err := client.ListLoginPolicySecondFactors(ctx, &admin.ListLoginPolicySecondFactorsRequest{}) + if err != nil { + return diag.Errorf("failed to get login policy secondfactors: %v", err) + } + if len(respSecond.GetResult()) > 0 { + factors := make([]string, 0) + for _, item := range respSecond.GetResult() { + factors = append(factors, item.String()) + } + set[secondFactorsVar] = factors + } + + respMulti, err := client.ListLoginPolicyMultiFactors(ctx, &admin.ListLoginPolicyMultiFactorsRequest{}) + if err != nil { + return diag.Errorf("failed to get login policy multifactors: %v", err) + } + if len(respMulti.GetResult()) > 0 { + factors := make([]string, 0) + for _, item := range respMulti.GetResult() { + factors = append(factors, item.String()) + } + set[multiFactorsVar] = factors + } + + respIDPs, err := client.ListLoginPolicyIDPs(ctx, &admin.ListLoginPolicyIDPsRequest{}) + if err != nil { + return diag.Errorf("failed to get login policy idps: %v", err) + } + if len(respIDPs.GetResult()) > 0 { + idps := make([]string, 0) + for _, idpItem := range respIDPs.GetResult() { + idps = append(idps, idpItem.IdpId) + } + set[idpsVar] = idps + } + + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of login policy: %v", k, err) + } + } + d.SetId(resp.Policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/default_login_policy/resource.go b/zitadel/default_login_policy/resource.go new file mode 100644 index 00000000..a8a383c3 --- /dev/null +++ b/zitadel/default_login_policy/resource.go @@ -0,0 +1,129 @@ +package default_login_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the default login policy.", + Schema: map[string]*schema.Schema{ + allowUsernamePasswordVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if a user is allowed to login with his username and password", + }, + allowRegisterVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if a person is allowed to register a user on this organisation", + }, + allowExternalIDPVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if a user is allowed to add a defined identity provider. E.g. Google auth", + }, + forceMFAVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if a user MUST use a multi factor to log in", + }, + forceMFALocalOnlyVar: { + Type: schema.TypeBool, + Required: true, + Description: "if activated, ZITADEL only enforces MFA on local authentications. On authentications through MFA, ZITADEL won't prompt for MFA.", + }, + passwordlessTypeVar: { + Type: schema.TypeString, + Required: true, + Description: "defines if passwordless is allowed for users", + }, + hidePasswordResetVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if password reset link should be shown in the login screen", + }, + ignoreUnknownUsernamesVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if unknown username on login screen directly return an error or always display the password screen", + }, + DefaultRedirectURIVar: { + Type: schema.TypeString, + Required: true, + Description: "defines where the user will be redirected to if the login is started without app context (e.g. from mail)", + }, + passwordCheckLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + externalLoginCheckLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + mfaInitSkipLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + secondFactorCheckLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + multiFactorCheckLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + secondFactorsVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "allowed second factors", + }, + multiFactorsVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "allowed multi factors", + }, + idpsVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "allowed idps to login or register", + }, + allowDomainDiscovery: { + Type: schema.TypeBool, + Optional: true, + Description: "if set to true, the suffix (@domain.com) of an unknown username input on the login screen will be matched against the org domains and will redirect to the registration of that organisation on success.", + }, + disableLoginWithEmail: { + Type: schema.TypeBool, + Optional: true, + Description: "defines if user can additionally (to the loginname) be identified by their verified email address", + }, + disableLoginWithPhone: { + Type: schema.TypeBool, + Optional: true, + Description: "defines if user can additionally (to the loginname) be identified by their verified phone number", + }, + }, + CreateContext: update, + UpdateContext: update, + DeleteContext: delete, + ReadContext: read, + Importer: helper.ImportWithEmptyID(), + } +} diff --git a/zitadel/default_login_policy/resource_test.go b/zitadel/default_login_policy/resource_test.go new file mode 100644 index 00000000..14b4d0aa --- /dev/null +++ b/zitadel/default_login_policy/resource_test.go @@ -0,0 +1,53 @@ +package default_login_policy_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_login_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_azure_ad/idp_azure_ad_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_google/idp_google_test_dep" +) + +func TestAccDefaultLoginPolicy(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_login_policy") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, default_login_policy.DefaultRedirectURIVar, exampleAttributes).AsString() + azureADDep, _ := idp_azure_ad_test_dep.Create(t, frame.BaseTestFrame, frame) + googleDep, _ := idp_google_test_dep.Create(t, frame.BaseTestFrame, frame) + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{azureADDep, googleDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "localhost:9090", + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckNothing, + test_utils.ImportNothing, + ) +} + +func checkRemoteProperty(frame test_utils.InstanceTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetLoginPolicy(frame, &admin.GetLoginPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetDefaultRedirectUri() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_login_texts/resource.go b/zitadel/default_login_texts/resource.go new file mode 100644 index 00000000..eabf46f6 --- /dev/null +++ b/zitadel/default_login_texts/resource.go @@ -0,0 +1,234 @@ +package default_login_texts + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &defaultLoginTextsResource{} +) + +func New() resource.Resource { + return &defaultLoginTextsResource{} +} + +type defaultLoginTextsResource struct { + clientInfo *helper.ClientInfo +} + +func (r *defaultLoginTextsResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_default_login_texts" +} + +func (r *defaultLoginTextsResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + s, d := text.GenSchemaLoginCustomText(ctx) + delete(s.Attributes, "org_id") + return s, d +} + +func (r *defaultLoginTextsResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *defaultLoginTextsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.LoginCustomText{} + resp.Diagnostics.Append(text.CopyLoginCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal login texts", err.Error()) + return + } + zReq := &admin.SetCustomLoginTextsRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal login texts", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomLoginText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create login texts", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultLoginTextsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + language := getID(ctx, state) + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomLoginTexts(ctx, &admin.GetCustomLoginTextsRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyLoginCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *defaultLoginTextsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.LoginCustomText{} + resp.Diagnostics.Append(text.CopyLoginCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal login texts", err.Error()) + return + } + zReq := &admin.SetCustomLoginTextsRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal login texts", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomLoginText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update login texts", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultLoginTextsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomLoginTextToDefault(ctx, &admin.ResetCustomLoginTextsToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete login texts", err.Error()) + return + } +} + +func setID(obj types.Object, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(language) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) string { + return helper.GetStringFromAttr(ctx, obj.Attributes(), "id") +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) string { + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) string { + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} diff --git a/zitadel/default_login_texts/resource_test.go b/zitadel/default_login_texts/resource_test.go new file mode 100644 index 00000000..a6bb1b92 --- /dev/null +++ b/zitadel/default_login_texts/resource_test.go @@ -0,0 +1,51 @@ +package default_login_texts_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_login_texts" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultLoginTexts(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_login_texts") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := "example" + exampleLanguage := test_utils.AttributeValue(t, default_login_texts.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtext", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)(""), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.InstanceTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomLoginTexts(frame, &admin.GetCustomLoginTextsRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetEmailVerificationDoneText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_notification_policy/const.go b/zitadel/default_notification_policy/const.go new file mode 100644 index 00000000..a2447ba7 --- /dev/null +++ b/zitadel/default_notification_policy/const.go @@ -0,0 +1,5 @@ +package default_notification_policy + +const ( + passwordChangeVar = "password_change" +) diff --git a/zitadel/default_notification_policy/funcs.go b/zitadel/default_notification_policy/funcs.go new file mode 100644 index 00000000..4443ec59 --- /dev/null +++ b/zitadel/default_notification_policy/funcs.go @@ -0,0 +1,86 @@ +package default_notification_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "default notification policy cannot be deleted") + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChanges(passwordChangeVar) { + resp, err := client.UpdateNotificationPolicy(ctx, &admin.UpdateNotificationPolicyRequest{ + PasswordChange: d.Get(passwordChangeVar).(bool), + }) + if helper.IgnorePreconditionError(err) != nil { + return diag.Errorf("failed to update default notification policy: %v", err) + } + if resp != nil { + d.SetId(resp.GetDetails().GetResourceOwner()) + return nil + } + } + + resp, err := client.GetNotificationPolicy(ctx, &admin.GetNotificationPolicyRequest{}) + if err != nil { + return diag.Errorf("failed to update default notification policy: %v", err) + } + d.SetId(resp.GetPolicy().GetDetails().GetResourceOwner()) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetNotificationPolicy(ctx, &admin.GetNotificationPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get default notification policy") + } + policy := resp.Policy + set := map[string]interface{}{ + passwordChangeVar: policy.GetPasswordChange(), + } + + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of default notification policy: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/default_notification_policy/resource.go b/zitadel/default_notification_policy/resource.go new file mode 100644 index 00000000..ae8dbc02 --- /dev/null +++ b/zitadel/default_notification_policy/resource.go @@ -0,0 +1,25 @@ +package default_notification_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the default notification policy.", + Schema: map[string]*schema.Schema{ + passwordChangeVar: { + Type: schema.TypeBool, + Required: true, + Description: "Send notification if a user changes his password", + }, + }, + ReadContext: read, + CreateContext: update, + DeleteContext: delete, + UpdateContext: update, + Importer: helper.ImportWithEmptyID(), + } +} diff --git a/zitadel/default_notification_policy/resource_test.go b/zitadel/default_notification_policy/resource_test.go new file mode 100644 index 00000000..636af87e --- /dev/null +++ b/zitadel/default_notification_policy/resource_test.go @@ -0,0 +1,50 @@ +package default_notification_policy_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultNotificationPolicy(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_notification_policy") + resourceExample, _ := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := false + initialProperty := true + updatedProperty := false + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + initialProperty, updatedProperty, + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckNothing, + test_utils.ImportNothing, + ) +} + +func checkRemoteProperty(frame test_utils.InstanceTestFrame) func(bool) resource.TestCheckFunc { + return func(expect bool) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetNotificationPolicy(frame, &admin.GetNotificationPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetPasswordChange() + if actual != expect { + return fmt.Errorf("expected %t, but got %t", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_oidc_settings/const.go b/zitadel/default_oidc_settings/const.go new file mode 100644 index 00000000..6ce95434 --- /dev/null +++ b/zitadel/default_oidc_settings/const.go @@ -0,0 +1,8 @@ +package default_oidc_settings + +const ( + accessTokenLifetimeVar = "access_token_lifetime" + idTokenLifetimeVar = "id_token_lifetime" + refreshTokenIdleExpirationVar = "refresh_token_idle_expiration" + RefreshTokenExpirationVar = "refresh_token_expiration" +) diff --git a/zitadel/default_oidc_settings/datasource.go b/zitadel/default_oidc_settings/datasource.go new file mode 100644 index 00000000..798712ae --- /dev/null +++ b/zitadel/default_oidc_settings/datasource.go @@ -0,0 +1,34 @@ +package default_oidc_settings + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing the default oidc settings.", + Schema: map[string]*schema.Schema{ + accessTokenLifetimeVar: { + Type: schema.TypeString, + Computed: true, + Description: "lifetime duration of access tokens", + }, + idTokenLifetimeVar: { + Type: schema.TypeString, + Computed: true, + Description: "lifetime duration of id tokens", + }, + RefreshTokenExpirationVar: { + Type: schema.TypeString, + Computed: true, + Description: "expiration duration of refresh tokens", + }, + refreshTokenIdleExpirationVar: { + Type: schema.TypeString, + Computed: true, + Description: "expiration duration of idle refresh tokens", + }, + }, + ReadContext: read, + } +} diff --git a/zitadel/default_oidc_settings/funcs.go b/zitadel/default_oidc_settings/funcs.go new file mode 100644 index 00000000..1465ea38 --- /dev/null +++ b/zitadel/default_oidc_settings/funcs.go @@ -0,0 +1,103 @@ +package default_oidc_settings + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Warn(ctx, "default oidc settings cannot be deleted") + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + accessTokenLT, err := time.ParseDuration(d.Get(accessTokenLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + idTokenLT, err := time.ParseDuration(d.Get(idTokenLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + refreshTokenExp, err := time.ParseDuration(d.Get(RefreshTokenExpirationVar).(string)) + if err != nil { + return diag.FromErr(err) + } + refreshTokenIdleExp, err := time.ParseDuration(d.Get(refreshTokenIdleExpirationVar).(string)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.UpdateOIDCSettings(ctx, &admin.UpdateOIDCSettingsRequest{ + AccessTokenLifetime: durationpb.New(accessTokenLT), + IdTokenLifetime: durationpb.New(idTokenLT), + RefreshTokenIdleExpiration: durationpb.New(refreshTokenIdleExp), + RefreshTokenExpiration: durationpb.New(refreshTokenExp), + }) + id := resp.GetDetails().GetResourceOwner() + if err != nil { + if helper.IgnorePreconditionError(err) != nil { + return diag.Errorf("failed to update default oidc settings: %v", err) + } + } + if id == "" { + getResp, getErr := client.GetOIDCSettings(ctx, &admin.GetOIDCSettingsRequest{}) + if getErr != nil { + return diag.Errorf("failed to get new default oidc settings id: %v", getErr) + } + id = getResp.GetSettings().GetDetails().GetResourceOwner() + } + d.SetId(id) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetOIDCSettings(ctx, &admin.GetOIDCSettingsRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get default oidc settings") + } + + set := map[string]interface{}{ + accessTokenLifetimeVar: resp.GetSettings().GetAccessTokenLifetime().AsDuration().String(), + idTokenLifetimeVar: resp.GetSettings().GetIdTokenLifetime().AsDuration().String(), + refreshTokenIdleExpirationVar: resp.GetSettings().GetRefreshTokenIdleExpiration().AsDuration().String(), + RefreshTokenExpirationVar: resp.GetSettings().GetRefreshTokenExpiration().AsDuration().String(), + } + + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of default oidc settings: %v", k, err) + } + } + d.SetId(resp.GetSettings().GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/default_oidc_settings/resource.go b/zitadel/default_oidc_settings/resource.go new file mode 100644 index 00000000..70e2bb10 --- /dev/null +++ b/zitadel/default_oidc_settings/resource.go @@ -0,0 +1,40 @@ +package default_oidc_settings + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the default oidc settings.", + Schema: map[string]*schema.Schema{ + accessTokenLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "lifetime duration of access tokens", + }, + idTokenLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "lifetime duration of id tokens", + }, + RefreshTokenExpirationVar: { + Type: schema.TypeString, + Required: true, + Description: "expiration duration of refresh tokens", + }, + refreshTokenIdleExpirationVar: { + Type: schema.TypeString, + Required: true, + Description: "expiration duration of idle refresh tokens", + }, + }, + CreateContext: update, + UpdateContext: update, + DeleteContext: delete, + ReadContext: read, + Importer: helper.ImportWithEmptyID(), + } +} diff --git a/zitadel/default_oidc_settings/resource_test.go b/zitadel/default_oidc_settings/resource_test.go new file mode 100644 index 00000000..bb215903 --- /dev/null +++ b/zitadel/default_oidc_settings/resource_test.go @@ -0,0 +1,49 @@ +package default_oidc_settings_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_oidc_settings" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultOIDCSettings(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_oidc_settings") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, default_oidc_settings.RefreshTokenExpirationVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "456h0m0s", + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckNothing, + test_utils.ImportNothing, + ) +} + +func checkRemoteProperty(frame test_utils.InstanceTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetOIDCSettings(frame, &admin.GetOIDCSettingsRequest{}) + if err != nil { + return fmt.Errorf("getting oidc settings failed: %w", err) + } + actual := resp.GetSettings().GetRefreshTokenExpiration().AsDuration().String() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_password_change_message_text/resource.go b/zitadel/default_password_change_message_text/resource.go new file mode 100644 index 00000000..2ac3b630 --- /dev/null +++ b/zitadel/default_password_change_message_text/resource.go @@ -0,0 +1,234 @@ +package default_password_change_message_text + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &defaultPasswordChangeMessageTextResource{} +) + +func New() resource.Resource { + return &defaultPasswordChangeMessageTextResource{} +} + +type defaultPasswordChangeMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *defaultPasswordChangeMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_default_password_change_message_text" +} + +func (r *defaultPasswordChangeMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + s, d := text.GenSchemaMessageCustomText(ctx) + delete(s.Attributes, "org_id") + return s, d +} + +func (r *defaultPasswordChangeMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *defaultPasswordChangeMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultPasswordChangeMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultPasswordChangeMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultPasswordChangeMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + language := getID(ctx, state) + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomPasswordChangeMessageText(ctx, &admin.GetCustomPasswordChangeMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *defaultPasswordChangeMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultPasswordChangeMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultPasswordChangeMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultPasswordChangeMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomPasswordChangeMessageTextToDefault(ctx, &admin.ResetCustomPasswordChangeMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(language) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) string { + return helper.GetStringFromAttr(ctx, obj.Attributes(), "id") +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) string { + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) string { + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} diff --git a/zitadel/default_password_change_message_text/resource_test.go b/zitadel/default_password_change_message_text/resource_test.go new file mode 100644 index 00000000..60d37d96 --- /dev/null +++ b/zitadel/default_password_change_message_text/resource_test.go @@ -0,0 +1,51 @@ +package default_password_change_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_password_change_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultPasswordChangeMessageText(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_password_change_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + exampleLanguage := test_utils.AttributeValue(t, default_password_change_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtitle", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("ZITADEL - Password of user has changed"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.InstanceTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomPasswordChangeMessageText(frame, &admin.GetCustomPasswordChangeMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_password_complexity_policy/const.go b/zitadel/default_password_complexity_policy/const.go new file mode 100644 index 00000000..b6aaebac --- /dev/null +++ b/zitadel/default_password_complexity_policy/const.go @@ -0,0 +1,9 @@ +package default_password_complexity_policy + +const ( + MinLengthVar = "min_length" + hasUppercaseVar = "has_uppercase" + hasLowercaseVar = "has_lowercase" + hasNumberVar = "has_number" + hasSymbolVar = "has_symbol" +) diff --git a/zitadel/default_password_complexity_policy/funcs.go b/zitadel/default_password_complexity_policy/funcs.go new file mode 100644 index 00000000..c29a8daf --- /dev/null +++ b/zitadel/default_password_complexity_policy/funcs.go @@ -0,0 +1,97 @@ +package default_password_complexity_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "default password complexity policy cannot be deleted") + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + id := "" + if d.HasChanges(MinLengthVar, hasUppercaseVar, hasLowercaseVar, hasNumberVar, hasSymbolVar) { + resp, err := client.UpdatePasswordComplexityPolicy(ctx, &admin.UpdatePasswordComplexityPolicyRequest{ + MinLength: uint32(d.Get(MinLengthVar).(int)), + HasUppercase: d.Get(hasUppercaseVar).(bool), + HasLowercase: d.Get(hasLowercaseVar).(bool), + HasNumber: d.Get(hasNumberVar).(bool), + HasSymbol: d.Get(hasSymbolVar).(bool), + }) + if helper.IgnorePreconditionError(err) != nil { + return diag.Errorf("failed to update default password complexity policy: %v", err) + } + if resp != nil { + id = resp.GetDetails().GetResourceOwner() + } + } + if id == "" { + resp, err := client.GetPasswordComplexityPolicy(ctx, &admin.GetPasswordComplexityPolicyRequest{}) + if err != nil { + return diag.Errorf("failed to get default password complexity policy: %v", err) + } + id = resp.GetPolicy().GetDetails().GetResourceOwner() + } + d.SetId(id) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetPasswordComplexityPolicy(ctx, &admin.GetPasswordComplexityPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get default password complexity policy") + } + + policy := resp.Policy + set := map[string]interface{}{ + MinLengthVar: policy.GetMinLength(), + hasUppercaseVar: policy.GetHasUppercase(), + hasLowercaseVar: policy.GetHasLowercase(), + hasNumberVar: policy.GetHasNumber(), + hasSymbolVar: policy.GetHasSymbol(), + } + + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of default password complexity policy: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/default_password_complexity_policy/resource.go b/zitadel/default_password_complexity_policy/resource.go new file mode 100644 index 00000000..6ec20337 --- /dev/null +++ b/zitadel/default_password_complexity_policy/resource.go @@ -0,0 +1,45 @@ +package default_password_complexity_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the default password complexity policy.", + Schema: map[string]*schema.Schema{ + MinLengthVar: { + Type: schema.TypeInt, + Required: true, + Description: "Minimal length for the password", + }, + hasUppercaseVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if the password MUST contain an upper case letter", + }, + hasLowercaseVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if the password MUST contain a lower case letter", + }, + hasNumberVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if the password MUST contain a number", + }, + hasSymbolVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if the password MUST contain a symbol. E.g. \"$\"", + }, + }, + DeleteContext: delete, + ReadContext: read, + CreateContext: update, + UpdateContext: update, + Importer: helper.ImportWithEmptyID(), + } +} diff --git a/zitadel/default_password_complexity_policy/resource_test.go b/zitadel/default_password_complexity_policy/resource_test.go new file mode 100644 index 00000000..f7166d91 --- /dev/null +++ b/zitadel/default_password_complexity_policy/resource_test.go @@ -0,0 +1,53 @@ +package default_password_complexity_policy_test + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_password_complexity_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultPasswordComplexityPolicy(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_password_complexity_policy") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty, err := strconv.ParseUint(test_utils.AttributeValue(t, default_password_complexity_policy.MinLengthVar, exampleAttributes).AsString(), 10, 64) + if err != nil { + t.Fatalf("could not parse example property: %v", err) + } + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, 10, + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckNothing, + test_utils.ImportNothing, + ) +} + +func checkRemoteProperty(frame test_utils.InstanceTestFrame) func(uint64) resource.TestCheckFunc { + return func(expect uint64) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetPasswordComplexityPolicy(frame, &admin.GetPasswordComplexityPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetMinLength() + if actual != expect { + return fmt.Errorf("expected %d, but got %d", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_password_reset_message_text/resource.go b/zitadel/default_password_reset_message_text/resource.go new file mode 100644 index 00000000..f2b81d55 --- /dev/null +++ b/zitadel/default_password_reset_message_text/resource.go @@ -0,0 +1,234 @@ +package default_password_reset_message_text + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &defaultPasswordResetMessageTextResource{} +) + +func New() resource.Resource { + return &defaultPasswordResetMessageTextResource{} +} + +type defaultPasswordResetMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *defaultPasswordResetMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_default_password_reset_message_text" +} + +func (r *defaultPasswordResetMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + s, d := text.GenSchemaMessageCustomText(ctx) + delete(s.Attributes, "org_id") + return s, d +} + +func (r *defaultPasswordResetMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *defaultPasswordResetMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultPasswordResetMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultPasswordResetMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultPasswordResetMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + language := getID(ctx, state) + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomPasswordResetMessageText(ctx, &admin.GetCustomPasswordResetMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *defaultPasswordResetMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultPasswordResetMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultPasswordResetMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultPasswordResetMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomPasswordResetMessageTextToDefault(ctx, &admin.ResetCustomPasswordResetMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(language) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) string { + return helper.GetStringFromAttr(ctx, obj.Attributes(), "id") +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) string { + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) string { + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} diff --git a/zitadel/default_password_reset_message_text/resource_test.go b/zitadel/default_password_reset_message_text/resource_test.go new file mode 100644 index 00000000..c9d179f2 --- /dev/null +++ b/zitadel/default_password_reset_message_text/resource_test.go @@ -0,0 +1,51 @@ +package default_password_reset_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_password_reset_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultPassswordResetMessageText(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_password_reset_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + exampleLanguage := test_utils.AttributeValue(t, default_password_reset_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtitle", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("ZITADEL - Reset password"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.InstanceTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomPasswordResetMessageText(frame, &admin.GetCustomPasswordResetMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_passwordless_registration_message_text/resource.go b/zitadel/default_passwordless_registration_message_text/resource.go new file mode 100644 index 00000000..76eba735 --- /dev/null +++ b/zitadel/default_passwordless_registration_message_text/resource.go @@ -0,0 +1,234 @@ +package default_passwordless_registration_message_text + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &defaultPasswordlessRegistrationMessageTextResource{} +) + +func New() resource.Resource { + return &defaultPasswordlessRegistrationMessageTextResource{} +} + +type defaultPasswordlessRegistrationMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *defaultPasswordlessRegistrationMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_default_passwordless_registration_message_text" +} + +func (r *defaultPasswordlessRegistrationMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + s, d := text.GenSchemaMessageCustomText(ctx) + delete(s.Attributes, "org_id") + return s, d +} + +func (r *defaultPasswordlessRegistrationMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *defaultPasswordlessRegistrationMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultPasswordlessRegistrationMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultPasswordlessRegistrationMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultPasswordlessRegistrationMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + language := getID(ctx, state) + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomPasswordlessRegistrationMessageText(ctx, &admin.GetCustomPasswordlessRegistrationMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *defaultPasswordlessRegistrationMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultPasswordlessRegistrationMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultPasswordlessRegistrationMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultPasswordlessRegistrationMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomPasswordlessRegistrationMessageTextToDefault(ctx, &admin.ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(language) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) string { + return helper.GetStringFromAttr(ctx, obj.Attributes(), "id") +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) string { + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) string { + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} diff --git a/zitadel/default_passwordless_registration_message_text/resource_test.go b/zitadel/default_passwordless_registration_message_text/resource_test.go new file mode 100644 index 00000000..8164be16 --- /dev/null +++ b/zitadel/default_passwordless_registration_message_text/resource_test.go @@ -0,0 +1,51 @@ +package default_passwordless_registration_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_passwordless_registration_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultPasswordlessRegistrationMessageText(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_passwordless_registration_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + exampleLanguage := test_utils.AttributeValue(t, default_passwordless_registration_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtitle", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("ZITADEL - Add Passwordless Login"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.InstanceTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomPasswordlessRegistrationMessageText(frame, &admin.GetCustomPasswordlessRegistrationMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_privacy_policy/const.go b/zitadel/default_privacy_policy/const.go new file mode 100644 index 00000000..fc142b45 --- /dev/null +++ b/zitadel/default_privacy_policy/const.go @@ -0,0 +1,8 @@ +package default_privacy_policy + +const ( + tosLinkVar = "tos_link" + privacyLinkVar = "privacy_link" + HelpLinkVar = "help_link" + supportEmailVar = "support_email" +) diff --git a/zitadel/default_privacy_policy/funcs.go b/zitadel/default_privacy_policy/funcs.go new file mode 100644 index 00000000..e3e8b533 --- /dev/null +++ b/zitadel/default_privacy_policy/funcs.go @@ -0,0 +1,95 @@ +package default_privacy_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "default privacy policy cannot be deleted") + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + id := "" + if d.HasChanges(tosLinkVar, privacyLinkVar, HelpLinkVar, supportEmailVar) { + resp, err := client.UpdatePrivacyPolicy(ctx, &admin.UpdatePrivacyPolicyRequest{ + TosLink: d.Get(tosLinkVar).(string), + PrivacyLink: d.Get(privacyLinkVar).(string), + HelpLink: d.Get(HelpLinkVar).(string), + SupportEmail: d.Get(supportEmailVar).(string), + }) + if helper.IgnorePreconditionError(err) != nil { + return diag.Errorf("failed to update default privacy policy: %v", err) + } + if resp != nil { + id = resp.GetDetails().GetResourceOwner() + } + } + if id == "" { + resp, err := client.GetPrivacyPolicy(ctx, &admin.GetPrivacyPolicyRequest{}) + if err != nil { + return diag.Errorf("failed to update default privacy policy: %v", err) + } + id = resp.GetPolicy().GetDetails().GetResourceOwner() + } + d.SetId(id) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetPrivacyPolicy(ctx, &admin.GetPrivacyPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to default privacy policy") + } + + policy := resp.Policy + set := map[string]interface{}{ + tosLinkVar: policy.GetTosLink(), + privacyLinkVar: policy.GetPrivacyLink(), + HelpLinkVar: policy.GetHelpLink(), + supportEmailVar: policy.GetSupportEmail(), + } + + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of default privacy policy: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/default_privacy_policy/resource.go b/zitadel/default_privacy_policy/resource.go new file mode 100644 index 00000000..6944dff5 --- /dev/null +++ b/zitadel/default_privacy_policy/resource.go @@ -0,0 +1,40 @@ +package default_privacy_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the default privacy policy.", + Schema: map[string]*schema.Schema{ + tosLinkVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + privacyLinkVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + HelpLinkVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + supportEmailVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + }, + CreateContext: update, + DeleteContext: delete, + ReadContext: read, + UpdateContext: update, + Importer: helper.ImportWithEmptyID(), + } +} diff --git a/zitadel/default_privacy_policy/resource_test.go b/zitadel/default_privacy_policy/resource_test.go new file mode 100644 index 00000000..b36c2d56 --- /dev/null +++ b/zitadel/default_privacy_policy/resource_test.go @@ -0,0 +1,49 @@ +package default_privacy_policy_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_privacy_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultPrivacyPolicy(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_privacy_policy") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, default_privacy_policy.HelpLinkVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "http://example.com/acctest", + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckNothing, + test_utils.ImportNothing, + ) +} + +func checkRemoteProperty(frame test_utils.InstanceTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetPrivacyPolicy(frame, &admin.GetPrivacyPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetHelpLink() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_verify_email_message_text/resource.go b/zitadel/default_verify_email_message_text/resource.go new file mode 100644 index 00000000..1e0fa702 --- /dev/null +++ b/zitadel/default_verify_email_message_text/resource.go @@ -0,0 +1,234 @@ +package default_verify_email_message_text + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &defaultVerifyEmailMessageTextResource{} +) + +func New() resource.Resource { + return &defaultVerifyEmailMessageTextResource{} +} + +type defaultVerifyEmailMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *defaultVerifyEmailMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_default_verify_email_message_text" +} + +func (r *defaultVerifyEmailMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + s, d := text.GenSchemaMessageCustomText(ctx) + delete(s.Attributes, "org_id") + return s, d +} + +func (r *defaultVerifyEmailMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *defaultVerifyEmailMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultVerifyEmailMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultVerifyEmailMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultVerifyEmailMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + language := getID(ctx, state) + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomVerifyEmailMessageText(ctx, &admin.GetCustomVerifyEmailMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *defaultVerifyEmailMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultVerifyEmailMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultVerifyEmailMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultVerifyEmailMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomVerifyEmailMessageTextToDefault(ctx, &admin.ResetCustomVerifyEmailMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(language) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) string { + return helper.GetStringFromAttr(ctx, obj.Attributes(), "id") +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) string { + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) string { + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} diff --git a/zitadel/default_verify_email_message_text/resource_test.go b/zitadel/default_verify_email_message_text/resource_test.go new file mode 100644 index 00000000..49b58448 --- /dev/null +++ b/zitadel/default_verify_email_message_text/resource_test.go @@ -0,0 +1,51 @@ +package default_verify_email_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_verify_email_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultVerifyEmailMessageText(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_verify_email_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + exampleLanguage := test_utils.AttributeValue(t, default_verify_email_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtitle", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("ZITADEL - Verify email"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.InstanceTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomVerifyEmailMessageText(frame, &admin.GetCustomVerifyEmailMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/default_verify_phone_message_text/resource.go b/zitadel/default_verify_phone_message_text/resource.go new file mode 100644 index 00000000..b6e44e87 --- /dev/null +++ b/zitadel/default_verify_phone_message_text/resource.go @@ -0,0 +1,234 @@ +package default_verify_phone_message_text + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &defaultVerifyPhoneMessageTextResource{} +) + +func New() resource.Resource { + return &defaultVerifyPhoneMessageTextResource{} +} + +type defaultVerifyPhoneMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *defaultVerifyPhoneMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_default_verify_phone_message_text" +} + +func (r *defaultVerifyPhoneMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + s, d := text.GenSchemaMessageCustomText(ctx) + delete(s.Attributes, "org_id") + return s, d +} + +func (r *defaultVerifyPhoneMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *defaultVerifyPhoneMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultVerifyPhoneMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultVerifyPhoneMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultVerifyPhoneMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + language := getID(ctx, state) + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomVerifyPhoneMessageText(ctx, &admin.GetCustomVerifyPhoneMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *defaultVerifyPhoneMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &admin.SetDefaultVerifyPhoneMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetDefaultVerifyPhoneMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *defaultVerifyPhoneMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetAdminClient(r.clientInfo) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomVerifyPhoneMessageTextToDefault(ctx, &admin.ResetCustomVerifyPhoneMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(language) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) string { + return helper.GetStringFromAttr(ctx, obj.Attributes(), "id") +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) string { + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) string { + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "" + } + return language +} diff --git a/zitadel/default_verify_phone_message_text/resource_test.go b/zitadel/default_verify_phone_message_text/resource_test.go new file mode 100644 index 00000000..02bf80e1 --- /dev/null +++ b/zitadel/default_verify_phone_message_text/resource_test.go @@ -0,0 +1,51 @@ +package default_verify_phone_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_verify_phone_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDefaultVerifyPhoneMessageText(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_default_verify_phone_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + exampleLanguage := test_utils.AttributeValue(t, default_verify_phone_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtitle", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("ZITADEL - Verify phone"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.InstanceTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomVerifyPhoneMessageText(frame, &admin.GetCustomVerifyPhoneMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/domain/const.go b/zitadel/domain/const.go new file mode 100644 index 00000000..fd6b95e9 --- /dev/null +++ b/zitadel/domain/const.go @@ -0,0 +1,8 @@ +package domain + +const ( + NameVar = "name" + isVerifiedVar = "is_verified" + isPrimaryVar = "is_primary" + validationTypeVar = "validation_type" +) diff --git a/zitadel/domain/funcs.go b/zitadel/domain/funcs.go new file mode 100644 index 00000000..e14134f8 --- /dev/null +++ b/zitadel/domain/funcs.go @@ -0,0 +1,163 @@ +package domain + +import ( + "context" + "strings" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/object" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/org" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + domainName := d.Id() + if d.Get(isPrimaryVar).(bool) { + resp, err := client.ListOrgDomains(ctx, &management.ListOrgDomainsRequest{}) + if err != nil { + return diag.FromErr(err) + } + for _, domain := range resp.Result { + parts := strings.Split(clientinfo.Domain, ":") + if domain.IsVerified && domain.DomainName != domainName && strings.HasSuffix(domain.GetDomainName(), parts[0]) { + if _, err := client.SetPrimaryOrgDomain(ctx, &management.SetPrimaryOrgDomainRequest{Domain: domain.DomainName}); err != nil { + return diag.FromErr(err) + } + break + } + } + } + + _, err = client.RemoveOrgDomain(ctx, &management.RemoveOrgDomainRequest{ + Domain: domainName, + }) + if err != nil { + return diag.Errorf("failed to delete domain: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + name := d.Get(NameVar).(string) + _, err = client.AddOrgDomain(ctx, &management.AddOrgDomainRequest{ + Domain: name, + }) + if err != nil { + return diag.Errorf("failed to create domain: %v", err) + } + d.SetId(name) + if d.Get(isPrimaryVar).(bool) { + _, err = client.SetPrimaryOrgDomain(ctx, &management.SetPrimaryOrgDomainRequest{Domain: name}) + if err != nil { + return diag.Errorf("failed to set domain primary: %v", err) + } + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + name := d.Get(NameVar).(string) + d.SetId(name) + if d.HasChange(isPrimaryVar) { + if d.Get(isPrimaryVar).(bool) { + _, err = client.SetPrimaryOrgDomain(ctx, &management.SetPrimaryOrgDomainRequest{Domain: name}) + if err != nil { + return diag.Errorf("failed to set domain primary: %v", err) + } + } + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.ListOrgDomains(ctx, &management.ListOrgDomainsRequest{ + Queries: []*org.DomainSearchQuery{ + {Query: &org.DomainSearchQuery_DomainNameQuery{ + DomainNameQuery: &org.DomainNameQuery{ + Name: d.Id(), + Method: object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS, + }, + }, + }, + }, + }) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to list domains") + } + + if len(resp.Result) == 1 { + domain := resp.Result[0] + set := map[string]interface{}{ + NameVar: domain.GetDomainName(), + helper.OrgIDVar: domain.GetOrgId(), + isVerifiedVar: domain.GetIsVerified(), + isPrimaryVar: domain.GetIsPrimary(), + validationTypeVar: domain.GetValidationType().Number(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of domain: %v", k, err) + } + } + d.SetId(domain.GetDomainName()) + return nil + } + + d.SetId("") + return nil +} diff --git a/zitadel/domain/resource.go b/zitadel/domain/resource.go new file mode 100644 index 00000000..5a029457 --- /dev/null +++ b/zitadel/domain/resource.go @@ -0,0 +1,46 @@ +package domain + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a domain of the organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + NameVar: { + Type: schema.TypeString, + Required: true, + Description: "Name of the domain", + ForceNew: true, + }, + isVerifiedVar: { + Type: schema.TypeBool, + Computed: true, + Description: "Is domain verified", + }, + isPrimaryVar: { + Type: schema.TypeBool, + Optional: true, + Description: "Is domain primary", + Default: false, + }, + validationTypeVar: { + Type: schema.TypeInt, + Computed: true, + Description: "Validation type", + }, + }, + ReadContext: read, + CreateContext: create, + UpdateContext: update, + DeleteContext: delete, + Importer: helper.ImportWithAttributes( + helper.NewImportAttribute(NameVar, helper.ConvertNonEmpty, false), + helper.ImportOptionalOrgAttribute, + ), + } +} diff --git a/zitadel/domain/resource_test.go b/zitadel/domain/resource_test.go new file mode 100644 index 00000000..cb07c2c0 --- /dev/null +++ b/zitadel/domain/resource_test.go @@ -0,0 +1,61 @@ +package domain_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/org" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/domain" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDomain(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_domain") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, domain.NameVar, exampleAttributes).AsString() + updatedProperty := "updated.default.127.0.0.1.sslip.io" + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + true, + checkRemoteProperty(frame), + regexp.MustCompile(fmt.Sprintf(`^%s$|^%s$`, exampleProperty, updatedProperty)), + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(frame), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(_ *terraform.State) error { + remoteResource, err := frame.ListOrgDomains(frame, &management.ListOrgDomainsRequest{ + Queries: []*org.DomainSearchQuery{{ + Query: &org.DomainSearchQuery_DomainNameQuery{ + DomainNameQuery: &org.DomainNameQuery{ + Name: expect, + }, + }, + }}, + }) + if err != nil { + return err + } + if len(remoteResource.GetResult()) == 0 { + return fmt.Errorf("expected to find %s, but didn't: %w", expect, test_utils.ErrNotFound) + } + return nil + } + } +} diff --git a/zitadel/domain_claimed_message_text/resource.go b/zitadel/domain_claimed_message_text/resource.go new file mode 100644 index 00000000..1093d74e --- /dev/null +++ b/zitadel/domain_claimed_message_text/resource.go @@ -0,0 +1,251 @@ +package domain_claimed_message_text + +import ( + "context" + "strings" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &domainClaimedMessageTextResource{} +) + +func New() resource.Resource { + return &domainClaimedMessageTextResource{} +} + +type domainClaimedMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *domainClaimedMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_domain_claimed_message_text" +} + +func (r *domainClaimedMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return text.GenSchemaMessageCustomText(ctx) +} + +func (r *domainClaimedMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *domainClaimedMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomDomainClaimedMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomDomainClaimedMessageCustomText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *domainClaimedMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + orgID, language := getID(ctx, state) + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomDomainClaimedMessageText(ctx, &management.GetCustomDomainClaimedMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *domainClaimedMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomDomainClaimedMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomDomainClaimedMessageCustomText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *domainClaimedMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + orgID, language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomDomainClaimedMessageTextToDefault(ctx, &management.ResetCustomDomainClaimedMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, orgID string, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(orgID + "_" + language) + attrs[helper.OrgIDVar] = types.StringValue(orgID) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) (string, string) { + id := helper.GetStringFromAttr(ctx, obj.Attributes(), "id") + parts := strings.Split(id, "_") + if len(parts) == 2 { + return parts[0], parts[1] + } + return helper.GetStringFromAttr(ctx, obj.Attributes(), helper.OrgIDVar), helper.GetStringFromAttr(ctx, obj.Attributes(), LanguageVar) +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(plan.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(state.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} diff --git a/zitadel/domain_claimed_message_text/resource_test.go b/zitadel/domain_claimed_message_text/resource_test.go new file mode 100644 index 00000000..92edab90 --- /dev/null +++ b/zitadel/domain_claimed_message_text/resource_test.go @@ -0,0 +1,51 @@ +package domain_claimed_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_domain_claimed_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDomainClaimedMessageText(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_domain_claimed_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + exampleLanguage := test_utils.AttributeValue(t, default_domain_claimed_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtitle", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^\d{18}_%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("Zitadel - Domain has been claimed"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomDomainClaimedMessageText(frame, &management.GetCustomDomainClaimedMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/domain_policy/const.go b/zitadel/domain_policy/const.go new file mode 100644 index 00000000..30bc49a2 --- /dev/null +++ b/zitadel/domain_policy/const.go @@ -0,0 +1,7 @@ +package domain_policy + +const ( + UserLoginMustBeDomainVar = "user_login_must_be_domain" + validateOrgDomainVar = "validate_org_domains" + smtpSenderVar = "smtp_sender_address_matches_instance_domain" +) diff --git a/zitadel/domain_policy/funcs.go b/zitadel/domain_policy/funcs.go new file mode 100644 index 00000000..4b6c5fc2 --- /dev/null +++ b/zitadel/domain_policy/funcs.go @@ -0,0 +1,132 @@ +package domain_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + org := d.Get(helper.OrgIDVar).(string) + + _, err = client.ResetCustomDomainPolicyToDefault(ctx, &admin.ResetCustomDomainPolicyToDefaultRequest{ + OrgId: org, + }) + if err != nil { + return diag.Errorf("failed to reset domain policy: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + org := helper.GetID(d, helper.OrgIDVar) + _, err = client.UpdateCustomDomainPolicy(ctx, &admin.UpdateCustomDomainPolicyRequest{ + OrgId: org, + UserLoginMustBeDomain: d.Get(UserLoginMustBeDomainVar).(bool), + ValidateOrgDomains: d.Get(validateOrgDomainVar).(bool), + SmtpSenderAddressMatchesInstanceDomain: d.Get(smtpSenderVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to update domain policy: %v", err) + } + d.SetId(org) + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + org := helper.GetID(d, helper.OrgIDVar) + _, err = client.AddCustomDomainPolicy(ctx, &admin.AddCustomDomainPolicyRequest{ + OrgId: org, + UserLoginMustBeDomain: d.Get(UserLoginMustBeDomainVar).(bool), + ValidateOrgDomains: d.Get(validateOrgDomainVar).(bool), + SmtpSenderAddressMatchesInstanceDomain: d.Get(smtpSenderVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to create domain policy: %v", err) + } + d.SetId(org) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetDomainPolicy(ctx, &management.GetDomainPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get domain policy") + } + + policy := resp.Policy + if policy.GetIsDefault() == true { + d.SetId("") + return nil + } + set := map[string]interface{}{ + helper.OrgIDVar: policy.GetDetails().GetResourceOwner(), + UserLoginMustBeDomainVar: policy.GetUserLoginMustBeDomain(), + validateOrgDomainVar: policy.GetValidateOrgDomains(), + smtpSenderVar: policy.GetSmtpSenderAddressMatchesInstanceDomain(), + } + + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of domain: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/domain_policy/resource.go b/zitadel/domain_policy/resource.go new file mode 100644 index 00000000..52b59951 --- /dev/null +++ b/zitadel/domain_policy/resource.go @@ -0,0 +1,36 @@ +package domain_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the custom domain policy of an organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + UserLoginMustBeDomainVar: { + Type: schema.TypeBool, + Required: true, + Description: "User login must be domain", + }, + validateOrgDomainVar: { + Type: schema.TypeBool, + Required: true, + Description: "Validate organization domains", + }, + smtpSenderVar: { + Type: schema.TypeBool, + Required: true, + Description: "", + }, + }, + ReadContext: read, + CreateContext: create, + DeleteContext: delete, + UpdateContext: update, + Importer: helper.ImportWithOptionalOrg(), + } +} diff --git a/zitadel/domain_policy/resource_test.go b/zitadel/domain_policy/resource_test.go new file mode 100644 index 00000000..d6f6f949 --- /dev/null +++ b/zitadel/domain_policy/resource_test.go @@ -0,0 +1,55 @@ +package domain_policy_test + +import ( + "fmt" + "strconv" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/domain_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccDomainPolicy(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_domain_policy") + otherFrame := frame.AnotherOrg(t, "domain-policy-org-"+frame.UniqueResourcesID) + resourceExample, resourceAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, domain_policy.UserLoginMustBeDomainVar, resourceAttributes).True() + test_utils.RunLifecyleTest( + t, + otherFrame.BaseTestFrame, + []string{otherFrame.AsOrgDefaultDependency}, + func(property bool, secret string) string { + // only replace first bool for the smtp_sender_address_matches_instance_domain property + return strings.Replace(resourceExample, strconv.FormatBool(exampleProperty), strconv.FormatBool(property), 1) + }, + exampleProperty, !exampleProperty, + "", "", "", + false, + checkRemoteProperty(*otherFrame), + helper.ZitadelGeneratedIdOnlyRegex, + checkRemoteProperty(*otherFrame)(false), + test_utils.ImportOrgId(otherFrame), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame) func(bool) resource.TestCheckFunc { + return func(expect bool) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetDomainPolicy(frame, &management.GetDomainPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetUserLoginMustBeDomain() + if actual != expect { + return fmt.Errorf("expected %t, but got %t", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/helper/client.go b/zitadel/helper/client.go new file mode 100644 index 00000000..96955b67 --- /dev/null +++ b/zitadel/helper/client.go @@ -0,0 +1,130 @@ +package helper + +import ( + "fmt" + + "github.com/zitadel/oidc/pkg/oidc" + "github.com/zitadel/zitadel-go/v2/pkg/client/admin" + "github.com/zitadel/zitadel-go/v2/pkg/client/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/middleware" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const ( + DomainVar = "domain" + InsecureVar = "insecure" + TokenVar = "token" + PortVar = "port" + JWTProfileFile = "jwt_profile_file" + JWTProfileJSON = "jwt_profile_json" +) + +type ClientInfo struct { + Domain string + Issuer string + KeyPath string + Data []byte + Options []zitadel.Option +} + +func GetClientInfo(insecure bool, domain string, token string, jwtProfileFile string, jwtProfileJSON string, port string) (*ClientInfo, error) { + options := []zitadel.Option{} + keyPath := "" + if token != "" { + options = append(options, zitadel.WithJWTProfileTokenSource(middleware.JWTProfileFromPath(token))) + keyPath = token + } else if jwtProfileFile != "" { + options = append(options, zitadel.WithJWTProfileTokenSource(middleware.JWTProfileFromPath(jwtProfileFile))) + keyPath = token + } else if jwtProfileJSON != "" { + options = append(options, zitadel.WithJWTProfileTokenSource(middleware.JWTProfileFromFileData([]byte(jwtProfileJSON)))) + } else { + return nil, fmt.Errorf("either 'jwt_profile_file' or 'jwt_profile_json' is required") + } + + issuerScheme := "https://" + if insecure { + options = append(options, zitadel.WithInsecure()) + issuerScheme = "http://" + } + + issuerPort := port + if port == "80" && insecure || port == "443" && !insecure { + issuerPort = "" + } + + issuer := issuerScheme + domain + if issuerPort != "" { + issuer += ":" + issuerPort + } + + clientDomain := domain + ":" + port + if port == "" { + clientDomain = domain + ":443" + if insecure { + clientDomain = domain + ":80" + } + } + + return &ClientInfo{ + clientDomain, + issuer, + keyPath, + []byte(jwtProfileJSON), + options, + }, nil +} + +func GetAdminClient(info *ClientInfo) (*admin.Client, error) { + client, err := admin.NewClient( + info.Issuer, info.Domain, + []string{oidc.ScopeOpenID, zitadel.ScopeZitadelAPI()}, + info.Options..., + ) + if err != nil { + return nil, fmt.Errorf("failed to start zitadel client: %v", err) + } + + return client, nil +} + +func GetManagementClient(info *ClientInfo, orgID string) (*management.Client, error) { + options := info.Options + if orgID != "" { + options = append(options, zitadel.WithOrgID(orgID)) + } + + client, err := management.NewClient( + info.Issuer, info.Domain, + []string{oidc.ScopeOpenID, zitadel.ScopeZitadelAPI()}, + options..., + ) + if err != nil { + return nil, fmt.Errorf("failed to start zitadel client: %v", err) + } + return client, nil +} + +func IgnoreIfNotFoundError(err error) error { + //permission denied included as nothing can be found then as well + if code := status.Code(err); code == codes.NotFound || code == codes.PermissionDenied { + return nil + } + return err +} + +func IgnorePreconditionError(err error) error { + if code := status.Code(err); code == codes.FailedPrecondition { + return nil + } + return err +} + +func IgnoreAlreadyExistsError(err error) error { + if code := status.Code(err); code == codes.AlreadyExists { + return nil + } + return err +} diff --git a/zitadel/helper/form.go b/zitadel/helper/form.go new file mode 100644 index 00000000..d5ee76ae --- /dev/null +++ b/zitadel/helper/form.go @@ -0,0 +1,141 @@ +package helper + +import ( + "bytes" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/textproto" + "os" + "path/filepath" + "strings" + + "github.com/gabriel-vasile/mimetype" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/zitadel/oidc/pkg/client/profile" + "github.com/zitadel/oidc/pkg/oidc" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel" + "golang.org/x/oauth2" +) + +var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") + +func escapeQuotes(s string) string { + return quoteEscaper.Replace(s) +} + +func createMultipartRequest(issuer, endpoint, path string) (*http.Request, error) { + file, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("failed to read file: %v", err) + } + + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + data, err := io.ReadAll(file) + if err != nil { + return nil, fmt.Errorf("failed to read asset: %v", err) + } + + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", + fmt.Sprintf(`form-data; name="%s"; filename="%s"`, + escapeQuotes("file"), escapeQuotes(filepath.Base(file.Name())))) + h.Set("Content-Type", mimetype.Detect(data).String()) + part, err := writer.CreatePart(h) + if err != nil { + return nil, fmt.Errorf("failed to create asset part: %v", err) + } + io.Copy(part, bytes.NewBuffer(data)) + writer.Close() + + r, err := http.NewRequest(http.MethodPost, issuer+endpoint, body) + if err != nil { + return nil, fmt.Errorf("failed to create asset request: %v", err) + } + + r.Header.Add("Content-Type", writer.FormDataContentType()) + return r, nil +} + +func InstanceFormFilePost(clientInfo *ClientInfo, endpoint, path string) diag.Diagnostics { + return formFilePost(clientInfo, endpoint, path, map[string]string{}) +} + +func OrgFormFilePost(clientInfo *ClientInfo, endpoint, path, orgID string) diag.Diagnostics { + return formFilePost(clientInfo, endpoint, path, map[string]string{"x-zitadel-orgid": orgID}) +} + +func formFilePost(clientInfo *ClientInfo, endpoint, path string, additionalHeaders map[string]string) diag.Diagnostics { + var client *http.Client + r, err := createMultipartRequest(clientInfo.Issuer, endpoint, path) + if err != nil { + return diag.Errorf("failed to create asset request: %v", err) + } + for k, v := range additionalHeaders { + r.Header.Add(k, v) + } + + if clientInfo.KeyPath != "" { + client, err = NewClientWithInterceptorFromKeyFile(clientInfo.Issuer, clientInfo.KeyPath, []string{oidc.ScopeOpenID, zitadel.ScopeZitadelAPI()}) + if err != nil { + return diag.Errorf("failed to create client: %v", err) + } + } else if len(clientInfo.Data) > 0 { + client, err = NewClientWithInterceptorFromKeyFileData(clientInfo.Issuer, clientInfo.Data, []string{oidc.ScopeOpenID, zitadel.ScopeZitadelAPI()}) + if err != nil { + return diag.Errorf("failed to create client: %v", err) + } + } else { + return diag.Errorf("either 'jwt_profile_file' or 'jwt_profile_json' is required") + } + + resp, err := client.Do(r) + if err != nil || resp.StatusCode != http.StatusOK { + return diag.Errorf("failed to do asset request: %v", err) + } + return nil +} + +type Interceptor struct { + tokenSource oauth2.TokenSource + core http.RoundTripper +} + +func NewClientWithInterceptorFromKeyFile(issuer, keyPath string, scopes []string) (*http.Client, error) { + ts, err := profile.NewJWTProfileTokenSourceFromKeyFile(issuer, keyPath, scopes) + if err != nil { + return nil, err + } + + return &http.Client{ + Transport: Interceptor{core: http.DefaultTransport, tokenSource: ts}, + }, nil +} + +func NewClientWithInterceptorFromKeyFileData(issuer string, data []byte, scopes []string) (*http.Client, error) { + ts, err := profile.NewJWTProfileTokenSourceFromKeyFileData(issuer, data, scopes) + if err != nil { + return nil, err + } + + return &http.Client{ + Transport: Interceptor{core: http.DefaultTransport, tokenSource: ts}, + }, nil +} + +func (i Interceptor) RoundTrip(r *http.Request) (*http.Response, error) { + defer func() { + _ = r.Body.Close() + }() + + ts := oauth2.ReuseTokenSource(nil, i.tokenSource) + + token, err := ts.Token() + if err != nil { + return nil, err + } + r.Header.Set("authorization", token.TokenType+" "+token.AccessToken) + return i.core.RoundTrip(r) +} diff --git a/zitadel/helper/helper.go b/zitadel/helper/helper.go new file mode 100644 index 00000000..85a54e52 --- /dev/null +++ b/zitadel/helper/helper.go @@ -0,0 +1,140 @@ +package helper + +import ( + "context" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type Stringified struct { + Str string +} + +func (s *Stringified) String() string { + return s.Str +} + +type Stringify interface { + String() string +} + +func GetOkSetToStringSlice(d *schema.ResourceData, value string) []string { + var slice []string + if set, ok := d.GetOk(value); ok { + slice = SetToStringSlice(set.(*schema.Set)) + } else { + slice = make([]string, 0) + } + return slice +} + +func SetToStringSlice(set *schema.Set) []string { + slice := make([]string, 0) + for _, secondFactor := range set.List() { + slice = append(slice, secondFactor.(string)) + } + return slice +} + +func GetAddAndDelete(current []string, desired []string) ([]string, []string) { + addSlice := make([]string, 0) + deleteSlice := make([]string, 0) + + for _, desiredItem := range desired { + found := false + for _, currentItem := range current { + if desiredItem == currentItem { + found = true + } + } + if !found { + addSlice = append(addSlice, desiredItem) + } + } + + for _, currentItem := range current { + found := false + for _, desiredItem := range desired { + if desiredItem == currentItem { + found = true + } + } + if !found { + deleteSlice = append(deleteSlice, currentItem) + } + } + + return addSlice, deleteSlice +} + +func EnumValuesValidation(ty string, checkValuesSet interface{}, enumValues map[string]int32) diag.Diagnostics { + values, ok := checkValuesSet.(*schema.Set) + if !ok { + return diag.Errorf("Attribute %s is no set for enum value check", ty) + } + + for _, value := range values.List() { + _, ok := enumValues[value.(string)] + if !ok { + return diag.Errorf("Attribute %s has unsupported enum value \"%s\"", ty, value) + } + } + return nil +} + +func EnumValueValidation(ty string, checkValue interface{}, enumValues map[string]int32) diag.Diagnostics { + value, ok := checkValue.(string) + if !ok { + return diag.Errorf("Attribute %s is no string for enum value check", ty) + } + + _, ok = enumValues[value] + if !ok { + return diag.Errorf("Attribute %s has unsupported enum value \"%s\"", ty, value) + } + return nil +} + +func GetID(d *schema.ResourceData, idVar string) string { + idStr := "" + id, ok := d.GetOk(idVar) + if ok { + idStr = id.(string) + } else { + idStr = d.Id() + } + return idStr +} + +func GetStringFromAttr(ctx context.Context, attrs map[string]attr.Value, key string) string { + value, err := attrs[key].ToTerraformValue(ctx) + if err != nil { + return "" + } + var str string + if err := value.As(&str); err != nil { + return "" + } + return str +} + +func DescriptionEnumValuesList(enum map[int32]string) string { + str := ", supported values: " + values := make([]string, len(enum)) + for i := 0; i < len(enum); i++ { + values[i] = enum[int32(i)] + } + str += strings.Join(values, ", ") + return str +} + +func EnumValueMap(enum map[int32]string) map[string]int32 { + values := make(map[string]int32) + for k, v := range enum { + values[v] = k + } + return values +} diff --git a/zitadel/helper/import.go b/zitadel/helper/import.go new file mode 100644 index 00000000..4ff96c21 --- /dev/null +++ b/zitadel/helper/import.go @@ -0,0 +1,220 @@ +package helper + +import ( + "context" + "encoding/csv" + "encoding/json" + "errors" + "fmt" + "io" + "sort" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var ( + ImportOptionalOrgAttribute = NewImportAttribute(OrgIDVar, ConvertID, true) + emptyIDAttribute = NewImportAttribute(`""`, ConvertEmpty, false) + SemicolonPlaceholder = "__SEMICOLON__" +) + +func NewImportAttribute(key string, value ConvertStringFunc, optional bool) importAttribute { + return importAttribute{key: key, value: value, optional: optional} +} + +// ImportWithID is a convenience function that calls ImportWithAttributes. +// It returns a ResourceImporter that expects a ZITADEL ID number at the first import string position along with other given attributes. +// idVar is only relevant for the error message, the resources SetID function is called with first argument ID +func ImportWithID(idVar string, attributes ...importAttribute) *schema.ResourceImporter { + return ImportWithAttributes(append([]importAttribute{NewImportAttribute(idVar, ConvertID, false)}, attributes...)...) +} + +// ImportWithOptionalOrg is a convenience function that calls ImportWithAttributes. +// It returns a ResourceImporter that accepts an optional organization id along with other given attributes +func ImportWithOptionalOrg(attributes ...importAttribute) *schema.ResourceImporter { + return ImportWithAttributes(append([]importAttribute{ImportOptionalOrgAttribute}, attributes...)...) +} + +// ImportWithIDAndOptionalOrg is a convenience function that calls ImportWithID +// and passes an optional attribute for the org ID along with the other given attributes. +func ImportWithIDAndOptionalOrg(idVar string, attributes ...importAttribute) *schema.ResourceImporter { + return ImportWithID(idVar, append(attributes, ImportOptionalOrgAttribute)...) +} + +// ImportWithIDAndOptionalSecret is a convenience function that calls ImportWithID +// and passes an optional attribute for the secret var at secretKey. +func ImportWithIDAndOptionalSecret(idVar, secretKey string) *schema.ResourceImporter { + return ImportWithID(idVar, importAttribute{key: secretKey, value: ConvertNonEmpty, optional: true}) +} + +// ImportWithIDAndOptionalOrgAndSecret is a convenience function that calls ImportWithIDAndOptionalOrg +// and passes an optional attribute for the secret var at secretKey. +func ImportWithIDAndOptionalOrgAndSecret(idVar, secretKey string) *schema.ResourceImporter { + return ImportWithIDAndOptionalOrg(idVar, importAttribute{key: secretKey, value: ConvertNonEmpty, optional: true}) +} + +// ImportWithEmptyID returns a ResourceImporter that does not use the first import string position value +// for the states SetID call. It uses a dummy value, instead. +func ImportWithEmptyID(attributes ...importAttribute) *schema.ResourceImporter { + return ImportWithAttributes(append([]importAttribute{emptyIDAttribute}, attributes...)...) +} + +type ConvertStringFunc func(string) (interface{}, error) + +type importAttribute struct { + key string + value ConvertStringFunc + optional bool +} + +type ImportAttributes []importAttribute + +// Less makes the attributes sortable by putting the optional attributes to the end +// and the org id to the beginning of the optional attributes +func (i ImportAttributes) Less(j, k int) bool { + left := (i)[j] + right := (i)[k] + if !left.optional && right.optional { + return true + } + if left.optional && right.optional && left.key == OrgIDVar { + return true + } + return false +} + +func (i ImportAttributes) Len() int { return len(i) } + +func (i ImportAttributes) Swap(j, k int) { (i)[j], (i)[k] = (i)[k], (i)[j] } + +func ImportWithAttributes(attrs ...importAttribute) *schema.ResourceImporter { + return &schema.ResourceImporter{ + StateContext: func(_ context.Context, data *schema.ResourceData, i interface{}) (ret []*schema.ResourceData, err error) { + return []*schema.ResourceData{data}, importWithAttributes(data, attrs...) + }, + } +} + +type importState interface { + Id() string + SetId(string) + Set(string, interface{}) error +} + +// importWithAttributes imports a resources state that is needed to query the remote resource +// as well as state that is not readable from the ZITADEL API +func importWithAttributes(state importState, attrs ...importAttribute) (err error) { + id := state.Id() + var ( + optionalKeys []string + requiredKeys []string + ) + sort.Sort(ImportAttributes(attrs)) + for i, attr := range attrs { + if i == 0 && attr.key == `""` { + continue + } + if attr.optional { + optionalKeys = append(optionalKeys, attr.key) + } else { + requiredKeys = append(requiredKeys, attr.key) + } + } + defer func() { + err = ImportIDValidationError(id, requiredKeys, optionalKeys, err) + }() + externalMinParts := len(requiredKeys) + internalMinParts := externalMinParts + externalMaxParts := len(requiredKeys) + len(optionalKeys) + internalMaxParts := len(attrs) + csvReader := csv.NewReader(strings.NewReader(id)) + csvReader.Comma = ':' + csvReader.LazyQuotes = true + parts, err := csvReader.Read() + if err != nil && err != io.EOF { + return fmt.Errorf("failed to parse id: %w", err) + } + // if we expect an empty id and have more than just the emptyIDAttribute, we prepend an empty part to the ID + if len(attrs) > 0 && attrs[0].key == emptyIDAttribute.key || attrs[0].optional && len(parts) == 0 { + parts = append([]string{""}, parts...) + internalMinParts++ + } + if len(parts) < internalMinParts || len(parts) > internalMaxParts { + return fmt.Errorf(`expected the number of semicolon separated parts to be between %d and %d, but got %d parts: "%s"`, externalMinParts, externalMaxParts, len(parts), strings.Join(parts, `", "`)) + } + for i, part := range parts { + part = strings.ReplaceAll(part, SemicolonPlaceholder, ":") + attr := attrs[i] + // if the id is optional and not given, we use the emptyIDAttribute + if i == 0 && attr.optional && part == "" { + attr = emptyIDAttribute + } + val, err := attr.value(part) + if err != nil { + return fmt.Errorf("invalid value for %s: %w", attr.key, err) + } + if i == 0 { + state.SetId(val.(string)) + continue + } + if err := state.Set(attr.key, val); err != nil { + return fmt.Errorf("failed to set %s=%v: %w", attr.key, val, err) + } + } + return nil +} + +var _ ConvertStringFunc = ConvertID + +func ConvertID(id string) (interface{}, error) { + if !ZitadelGeneratedIdOnlyRegex.MatchString(id) { + return nil, fmt.Errorf(`id "%s" does not match regular expression %s`, id, ZitadelGeneratedIdOnlyRegex.String()) + } + return id, nil +} + +var _ ConvertStringFunc = ConvertJSON + +func ConvertJSON(importValue string) (interface{}, error) { + if err := json.Unmarshal([]byte(importValue), &struct{}{}); err != nil { + return nil, fmt.Errorf("value must be valid JSON: %w", err) + } + return importValue, nil +} + +var _ ConvertStringFunc = ConvertEmpty + +func ConvertEmpty(importValue string) (interface{}, error) { + if len(importValue) > 0 { + return nil, fmt.Errorf(`value must be empty, but got "%s"`, importValue) + } + return "imported", nil +} + +var _ ConvertStringFunc = ConvertNonEmpty + +func ConvertNonEmpty(importValue string) (interface{}, error) { + if len(importValue) == 0 { + return nil, errors.New("value must not be empty") + } + return importValue, nil +} + +// ImportIDValidationError wraps err with a help message about the expected format if it is not nil +func ImportIDValidationError(givenID string, requiredKeys, optionalKeys []string, err error) error { + if err == nil { + return nil + } + expectFormat := fmt.Sprintf("<") + if len(requiredKeys) > 0 { + expectFormat += fmt.Sprintf(":%s", strings.Join(requiredKeys, ":")) + } + if len(optionalKeys) > 0 { + expectFormat += fmt.Sprintf("[:%s]", strings.Join(optionalKeys, "][:")) + } + expectFormat = strings.Replace(expectFormat, "<:", "<", 1) + expectFormat = strings.Replace(expectFormat, "<[:", "<[", 1) + expectFormat += ">" + return fmt.Errorf(`failed to import id "%s" by format %s: %w`, givenID, expectFormat, err) +} diff --git a/zitadel/helper/import_test.go b/zitadel/helper/import_test.go new file mode 100644 index 00000000..06b280a1 --- /dev/null +++ b/zitadel/helper/import_test.go @@ -0,0 +1,292 @@ +package helper + +import ( + "fmt" + "reflect" + "strings" + "testing" +) + +func TestImportWithAttributes(t *testing.T) { + validID := "123456789012345678" + type args struct { + attrs []importAttribute + id string + } + type want struct { + attributes map[string]interface{} + expectErrorWithIDFormat string + expectErrorWithMinParts int + expectErrorWithMaxParts int + } + tests := []struct { + name string + args args + want want + }{{ + name: ` with '123...' works`, + args: args{ + attrs: []importAttribute{NewImportAttribute("id", ConvertID, false)}, + id: validID, + }, + want: want{ + attributes: map[string]interface{}{ + "id": validID, + }, + }, + }, { + name: ` with '' fails`, + args: args{ + attrs: []importAttribute{NewImportAttribute("id", ConvertID, false)}, + }, + want: want{ + expectErrorWithIDFormat: "", + expectErrorWithMinParts: 1, + expectErrorWithMaxParts: 1, + }, + }, { + name: ` with '123...:123...' works`, + args: args{ + attrs: []importAttribute{ + NewImportAttribute("id", ConvertID, false), + NewImportAttribute("required_id", ConvertID, false), + }, + id: concat(validID, validID), + }, + want: want{ + attributes: map[string]interface{}{ + "id": validID, + "required_id": validID, + }, + }, + }, { + name: ` with '123...' fails`, + args: args{ + attrs: []importAttribute{ + NewImportAttribute("id", ConvertID, false), + NewImportAttribute("required_id", ConvertID, false), + }, + id: validID, + }, + want: want{ + expectErrorWithIDFormat: "", + expectErrorWithMinParts: 2, + expectErrorWithMaxParts: 2, + }, + }, { + name: ` with '123...:123...:123...' works`, + args: args{ + attrs: []importAttribute{ + NewImportAttribute("id", ConvertID, false), + NewImportAttribute("required_id", ConvertID, false), + NewImportAttribute("optional_id", ConvertID, true), + }, + id: concat(validID, validID, validID), + }, + want: want{ + attributes: map[string]interface{}{ + "id": validID, + "required_id": validID, + "optional_id": validID, + }, + }, + }, { + name: ` with '123...:123...' works`, + args: args{ + attrs: []importAttribute{ + NewImportAttribute("id", ConvertID, false), + NewImportAttribute("required_id", ConvertID, false), + NewImportAttribute("optional_id", ConvertID, true), + }, + id: concat(validID, validID), + }, + want: want{ + attributes: map[string]interface{}{ + "id": validID, + "required_id": validID, + }, + }, + }, { + name: ` with '123...:123...:123...:123...' fails`, + args: args{ + attrs: []importAttribute{ + NewImportAttribute("id", ConvertID, false), + NewImportAttribute("required_id", ConvertID, false), + NewImportAttribute("optional_id", ConvertID, true), + }, + id: concat(validID, validID, validID, validID), + }, + want: want{ + expectErrorWithIDFormat: "", + expectErrorWithMinParts: 2, + expectErrorWithMaxParts: 3, + }, + }, { + name: `<> with '' works`, + args: args{ + attrs: []importAttribute{emptyIDAttribute}, + }, + want: want{ + attributes: map[string]interface{}{ + "id": "imported", + }, + }, + }, { + name: `<> with '123...' fails`, + args: args{ + attrs: []importAttribute{emptyIDAttribute}, + id: validID, + }, + want: want{ + expectErrorWithIDFormat: "<>", + expectErrorWithMinParts: -1, + expectErrorWithMaxParts: -1, + }, + }, { + name: `<[org_id]> with '123...' works`, + args: args{ + attrs: []importAttribute{ImportOptionalOrgAttribute}, + id: validID, + }, + want: want{ + attributes: map[string]interface{}{ + "id": validID, + }, + }, + }, { + name: `<[org_id]> with '' works`, + args: args{ + attrs: []importAttribute{ImportOptionalOrgAttribute}, + }, + want: want{ + attributes: map[string]interface{}{ + "id": "imported", + }, + }, + }, { + name: `<[org_id]> with 'invalid id' fails`, + args: args{ + attrs: []importAttribute{ImportOptionalOrgAttribute}, + id: "invalid id", + }, + want: want{ + expectErrorWithIDFormat: "<[org_id]>", + expectErrorWithMinParts: -1, + expectErrorWithMaxParts: -1, + }, + }, { + name: ` with empty id and '123...:123...' works`, + args: args{ + attrs: []importAttribute{ + emptyIDAttribute, + NewImportAttribute("required_id", ConvertID, false), + NewImportAttribute("optional_id", ConvertID, true), + }, + id: concat(validID, validID), + }, + want: want{ + attributes: map[string]interface{}{ + "id": "imported", + "required_id": validID, + "optional_id": validID, + }, + }, + }, { + name: ` with empty id and '123...:123...:123...' fails`, + args: args{ + attrs: []importAttribute{ + emptyIDAttribute, + NewImportAttribute("required_id", ConvertID, false), + NewImportAttribute("optional_id", ConvertID, true), + }, + id: concat(validID, validID, validID), + }, + want: want{ + expectErrorWithIDFormat: "", + expectErrorWithMinParts: 1, + expectErrorWithMaxParts: 2, + }, + }, { + name: ` with empty id and '' fails`, + args: args{ + attrs: []importAttribute{ + emptyIDAttribute, + NewImportAttribute("required_id", ConvertID, false), + NewImportAttribute("optional_id", ConvertID, true), + }, + }, + want: want{ + expectErrorWithIDFormat: "", + expectErrorWithMinParts: 1, + expectErrorWithMaxParts: 2, + }, + }, { + name: ` with empty id and '123...' fails`, + args: args{ + attrs: []importAttribute{ + emptyIDAttribute, + NewImportAttribute("required_id", ConvertID, false), + NewImportAttribute("another_required_id", ConvertID, false), + NewImportAttribute("optional_id", ConvertID, true), + }, + id: validID, + }, + want: want{ + expectErrorWithIDFormat: "", + expectErrorWithMinParts: 2, + expectErrorWithMaxParts: 3, + }, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + state := newMockState() + state.SetId(tt.args.id) + err := importWithAttributes(state, tt.args.attrs...) + wantAttributes := tt.want.attributes + if err != nil { + if tt.want.expectErrorWithIDFormat == "" { + t.Fatalf("importWithAttributes() error = %v, want %v", err, wantAttributes) + } + expectBetweenError := fmt.Sprintf("between %d and %d", tt.want.expectErrorWithMinParts, tt.want.expectErrorWithMaxParts) + if (tt.want.expectErrorWithMinParts > -1 || tt.want.expectErrorWithMaxParts > -1) && + !strings.Contains(err.Error(), expectBetweenError) { + t.Errorf(`expected error to contain "%s", got: %v`, expectBetweenError, err) + } + if !strings.Contains(err.Error(), tt.want.expectErrorWithIDFormat) { + t.Errorf("expected error to contain the expected format '%s', got: %v", tt.want.expectErrorWithIDFormat, err) + } + return + } + if tt.want.expectErrorWithIDFormat != "" { + t.Fatalf("expected error with format '%s', got state: %v", tt.want.expectErrorWithIDFormat, state) + } + if !reflect.DeepEqual(state, mockState(wantAttributes)) { + t.Errorf("importWithAttributes() = %v, want %v", state, wantAttributes) + } + }) + } +} + +func newMockState() mockState { return make(map[string]interface{}) } + +type mockState map[string]interface{} + +// SetId sets the ID of the state. +func (m mockState) SetId(id string) { + m["id"] = id +} + +// Id returns the ID of the state. +func (m mockState) Id() string { + return m["id"].(string) +} + +// Set sets the value of the given attribute. +func (m mockState) Set(key string, value interface{}) error { + m[key] = value + return nil +} + +func concat(attr ...string) string { + return strings.Join(attr, ":") +} diff --git a/zitadel/helper/schema.go b/zitadel/helper/schema.go new file mode 100644 index 00000000..c65583c2 --- /dev/null +++ b/zitadel/helper/schema.go @@ -0,0 +1,43 @@ +package helper + +import ( + "fmt" + "regexp" + + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + OrgIDVar = "org_id" +) + +var ( + // ZitadelGeneratedIdPattern matches IDs like 123456789012345678 + // ZITADEL IDs have 18 digits + ZitadelGeneratedIdPattern = `\d{18}` + ZitadelGeneratedIdOnlyRegex = regexp.MustCompile(fmt.Sprintf(`^%s$`, ZitadelGeneratedIdPattern)) + + OrgIDResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "ID of the organization", + ForceNew: true, + ValidateDiagFunc: func(i interface{}, path cty.Path) diag.Diagnostics { + _, err := ConvertID(i.(string)) + return diag.FromErr(err) + }, + } + + ResourceIDDatasourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "ID of the resource", + } + OrgIDDatasourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "ID of the organization", + } +) diff --git a/zitadel/helper/test_utils/base_frame.go b/zitadel/helper/test_utils/base_frame.go new file mode 100644 index 00000000..41b4cbad --- /dev/null +++ b/zitadel/helper/test_utils/base_frame.go @@ -0,0 +1,98 @@ +package test_utils + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-mux/tf5to6server" + "github.com/hashicorp/terraform-plugin-mux/tf6muxserver" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/zitadel/terraform-provider-zitadel/zitadel" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + insecure = true + port = "8080" + ExamplesResourceID = "123456789012345678" +) + +type BaseTestFrame struct { + context.Context + ClientInfo *helper.ClientInfo + ProviderSnippet, UniqueResourcesID string + ResourceType string + InstanceDomain string + TerraformName string + v6ProviderFactories map[string]func() (tfprotov6.ProviderServer, error) +} + +func NewBaseTestFrame(ctx context.Context, resourceType, domain string, jwtProfileJson []byte) (*BaseTestFrame, error) { + zitadelProvider := zitadel.Provider() + diag := zitadelProvider.Configure(ctx, terraform.NewResourceConfigRaw(map[string]interface{}{ + "domain": domain, + "insecure": insecure, + "port": port, + "jwt_profile_json": string(jwtProfileJson), + })) + if diag.HasError() { + return nil, fmt.Errorf("unknown error configuring the test provider: %v", diag) + } + providerSnippet := fmt.Sprintf(` +provider "zitadel" { + domain = "%s" + insecure = "%t" + port = "%s" + jwt_profile_json = < time.Minute { + return fmt.Errorf("function failed after retrying for a minute: %w", err) + } + time.Sleep(time.Second) + } +} diff --git a/zitadel/helper/test_utils/datasourcetest.go b/zitadel/helper/test_utils/datasourcetest.go new file mode 100644 index 00000000..be356fc0 --- /dev/null +++ b/zitadel/helper/test_utils/datasourcetest.go @@ -0,0 +1,31 @@ +package test_utils + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func RunDatasourceTest( + t *testing.T, + frame BaseTestFrame, + config string, + awaitRemoteResource resource.TestCheckFunc, + expectProperties map[string]string, +) { + var checks []resource.TestCheckFunc + if awaitRemoteResource != nil { + checks = append(checks, CheckAMinute(awaitRemoteResource)) + } + for k, v := range expectProperties { + checks = append(checks, resource.TestCheckResourceAttr("data."+frame.TerraformName, k, v)) + } + resource.ParallelTest(t, resource.TestCase{ + Steps: []resource.TestStep{{ + Config: fmt.Sprintf("%s\n%s", frame.ProviderSnippet, config), + Check: resource.ComposeAggregateTestCheckFunc(checks...), + }}, + ProtoV6ProviderFactories: frame.v6ProviderFactories, + }) +} diff --git a/zitadel/helper/test_utils/dependency.go b/zitadel/helper/test_utils/dependency.go new file mode 100644 index 00000000..df934b66 --- /dev/null +++ b/zitadel/helper/test_utils/dependency.go @@ -0,0 +1,15 @@ +package test_utils + +import ( + "strings" + "testing" +) + +func CreateDefaultDependency(t *testing.T, datasourceType string, idField string, newDependencyID func() (string, error)) (string, string) { + datasourceExample, datasourceExampleAttributes := ReadExample(t, Datasources, datasourceType) + dependencyID, err := newDependencyID() + if err != nil { + t.Fatalf("failed to create dependency for %s: %v", datasourceType, err) + } + return strings.Replace(datasourceExample, AttributeValue(t, idField, datasourceExampleAttributes).AsString(), dependencyID, 1), dependencyID +} diff --git a/zitadel/helper/test_utils/example.go b/zitadel/helper/test_utils/example.go new file mode 100644 index 00000000..daaf4cd3 --- /dev/null +++ b/zitadel/helper/test_utils/example.go @@ -0,0 +1,64 @@ +package test_utils + +import ( + "fmt" + "os" + "path" + "strings" + "testing" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclparse" + "github.com/zclconf/go-cty/cty" +) + +type examplesFolder string + +const ( + Datasources examplesFolder = "data-sources" + Resources examplesFolder = "resources" +) + +func ReadExample(t *testing.T, folder examplesFolder, exampleType string) (string, hcl.Attributes) { + fileName := strings.Replace(exampleType, "zitadel_", "", 1) + ".tf" + filePath := path.Join("..", "..", "examples", "provider", string(folder), fileName) + content, err := os.ReadFile(filePath) + if err != nil { + t.Fatalf("error reading example file: %v", err) + } + hclFile, diags := hclparse.NewParser().ParseHCL(content, filePath) + if diags.HasErrors() { + t.Fatalf("error parsing example file: %s", diags.Error()) + } + blocks := hclFile.BlocksAtPos(hcl.Pos{ + Line: 1, + Column: 1, + Byte: 1, + }) + if len(blocks) != 1 { + t.Fatalf("error parsing example file: %s", "unexpected number of blocks") + } + attr, diag := blocks[0].Body.JustAttributes() + if diag.HasErrors() { + t.Fatalf("error parsing example file: %s", diag.Error()) + } + return string(content), attr +} + +func AttributeValue(t *testing.T, key string, attributes hcl.Attributes) cty.Value { + val, diag := attributes[key].Expr.Value(&hcl.EvalContext{}) + if diag.HasErrors() { + t.Fatalf("error parsing example file: %s", diag.Error()) + } + return val +} + +func ReplaceAll[T comparable](resourceExample string, exampleProperty T, exampleSecret string) func(T, string) string { + return func(configProperty T, secretProperty string) string { + cfg := strings.ReplaceAll(resourceExample, fmt.Sprintf("%v", exampleProperty), fmt.Sprintf("%v", configProperty)) + if secretProperty != "" { + cfg = strings.Replace(cfg, exampleSecret, secretProperty, 1) + } + return cfg + } +} diff --git a/zitadel/helper/test_utils/import_id.go b/zitadel/helper/test_utils/import_id.go new file mode 100644 index 00000000..04798234 --- /dev/null +++ b/zitadel/helper/test_utils/import_id.go @@ -0,0 +1,62 @@ +package test_utils + +import ( + "fmt" + "regexp" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +var ( + _ resource.ImportStateIdFunc = ImportNothing + ZitadelGeneratedIdPattern = `\d{18}` + ZitadelGeneratedIdOnlyRegex = regexp.MustCompile(fmt.Sprintf(`^%s$`, ZitadelGeneratedIdPattern)) +) + +func ImportResourceId(frame BaseTestFrame) resource.ImportStateIdFunc { + return func(state *terraform.State) (string, error) { + return frame.State(state).ID, nil + } +} + +func ImportOrgId(frame *OrgTestFrame) resource.ImportStateIdFunc { + return func(state *terraform.State) (string, error) { + return frame.OrgID, nil + } +} + +func ImportStateAttribute(frame BaseTestFrame, attr string) resource.ImportStateIdFunc { + return func(state *terraform.State) (string, error) { + primary := frame.State(state) + val, ok := primary.Attributes[attr] + if !ok { + return "", fmt.Errorf("attribute %s not found in attributes %+v", attr, primary.Attributes) + } + if val != "" { + val = fmt.Sprintf(`"%s"`, strings.ReplaceAll(val, ":", helper.SemicolonPlaceholder)) + } + return val, nil + } +} + +func ImportNothing(_ *terraform.State) (string, error) { return "", nil } + +// ChainImportStateIdFuncs returns a function that composes an ID that has exactly the same +// length of semicolon separated parts as the passed number of funcs +func ChainImportStateIdFuncs(funcs ...resource.ImportStateIdFunc) resource.ImportStateIdFunc { + return func(state *terraform.State) (string, error) { + parts := make([]string, len(funcs)) + for i, f := range funcs { + part, err := f(state) + if err != nil { + return "", err + } + parts[i] = part + } + return strings.Join(parts, ":"), nil + } +} diff --git a/zitadel/helper/test_utils/instance_frame.go b/zitadel/helper/test_utils/instance_frame.go new file mode 100644 index 00000000..adbe3366 --- /dev/null +++ b/zitadel/helper/test_utils/instance_frame.go @@ -0,0 +1,33 @@ +package test_utils + +import ( + "context" + "testing" + + "github.com/zitadel/zitadel-go/v2/pkg/client/admin" + + "github.com/zitadel/terraform-provider-zitadel/acceptance" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +type InstanceTestFrame struct { + BaseTestFrame + *admin.Client +} + +func NewInstanceTestFrame(t *testing.T, resourceType string) *InstanceTestFrame { + ctx := context.Background() + cfg := acceptance.GetConfig().InstanceLevel + baseFrame, err := NewBaseTestFrame(ctx, resourceType, cfg.Domain, cfg.AdminSAJSON) + if err != nil { + t.Fatalf("setting up test context failed: %v", err) + } + adminClient, err := helper.GetAdminClient(baseFrame.ClientInfo) + if err != nil { + t.Fatalf("setting up test context failed: %v", err) + } + return &InstanceTestFrame{ + BaseTestFrame: *baseFrame, + Client: adminClient, + } +} diff --git a/zitadel/helper/test_utils/lifecyletest.go b/zitadel/helper/test_utils/lifecyletest.go new file mode 100644 index 00000000..928a3e24 --- /dev/null +++ b/zitadel/helper/test_utils/lifecyletest.go @@ -0,0 +1,85 @@ +package test_utils + +import ( + "fmt" + "os" + "regexp" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func RunLifecyleTest[P comparable]( + t *testing.T, + frame BaseTestFrame, + datasources []string, + resourceFunc func(property P, secret string) string, + exampleProperty, updatedProperty P, + secretAttribute, exampleSecret, updatedSecret string, + allowNonEmptyPlan bool, + checkRemoteProperty func(expect P) resource.TestCheckFunc, + idPattern *regexp.Regexp, + checkDestroy resource.TestCheckFunc, + importStateIdFunc resource.ImportStateIdFunc, + importStateVerifyIgnore ...string, +) { + exampleConfig := fmt.Sprintf("%s\n%s\n%s", frame.ProviderSnippet, strings.Join(datasources, "\n"), resourceFunc(exampleProperty, exampleSecret)) + updatedPropertyConfig := fmt.Sprintf("%s\n%s\n%s", frame.ProviderSnippet, strings.Join(datasources, "\n"), resourceFunc(updatedProperty, exampleSecret)) + steps := []resource.TestStep{ + { // Check first plan has a diff + Config: exampleConfig, + ExpectNonEmptyPlan: true, + // ExpectNonEmptyPlan just works with PlanOnly set to true + PlanOnly: true, + }, { // Check resource is created + Config: exampleConfig, + Check: resource.ComposeAggregateTestCheckFunc( + CheckAMinute(checkRemoteProperty(exampleProperty)), + CheckStateHasIDSet(frame, idPattern), + ), + }, { // Check updating name has a diff + Config: updatedPropertyConfig, + ExpectNonEmptyPlan: true, + // ExpectNonEmptyPlan just works with PlanOnly set to true + PlanOnly: true, + }, { // Check remote state can be updated + Config: updatedPropertyConfig, + Check: CheckAMinute(checkRemoteProperty(updatedProperty)), + }, + } + if importStateIdFunc != nil { + steps = append(steps, resource.TestStep{ // Expect importing works + Config: updatedPropertyConfig, + ResourceName: frame.TerraformName, + ImportState: true, + ImportStateIdFunc: importStateIdFunc, + ImportStateVerify: true, + ImportStateVerifyIgnore: importStateVerifyIgnore, + }) + } + if secretAttribute != "" { + updatedSecretConfig := fmt.Sprintf("%s\n%s\n%s", frame.ProviderSnippet, strings.Join(datasources, "\n"), resourceFunc(updatedProperty, updatedSecret)) + steps = append(steps, resource.TestStep{ // Check that secret has a diff + Config: updatedSecretConfig, + ExpectNonEmptyPlan: true, + // ExpectNonEmptyPlan just works with PlanOnly set to true + PlanOnly: true, + }, resource.TestStep{ // Check secret can be updated + Config: updatedSecretConfig, + }) + importStateVerifyIgnore = []string{secretAttribute} + } + resource.ParallelTest(t, resource.TestCase{ + CheckDestroy: CheckAMinute(checkDestroy), + Steps: steps, + ErrorCheck: func(err error) error { + if err != nil && allowNonEmptyPlan && os.Getenv("CI") == "true" && strings.Contains(err.Error(), "After applying this test step and performing a `terraform refresh`, the plan was not empty") { + t.Logf("Ignoring non-empty plan error because we can't guarantee consistency: %s", err.Error()) + return nil + } + return err + }, + ProtoV6ProviderFactories: frame.v6ProviderFactories, + }) +} diff --git a/zitadel/helper/test_utils/org_frame.go b/zitadel/helper/test_utils/org_frame.go new file mode 100644 index 00000000..61737f2a --- /dev/null +++ b/zitadel/helper/test_utils/org_frame.go @@ -0,0 +1,73 @@ +package test_utils + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/zitadel/zitadel-go/v2/pkg/client/admin" + mgmt "github.com/zitadel/zitadel-go/v2/pkg/client/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/acceptance" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +type OrgTestFrame struct { + BaseTestFrame + *mgmt.Client + Admin *admin.Client + OrgID string + AsOrgDefaultDependency string +} + +func (o *OrgTestFrame) useOrgContext(orgID string) (err error) { + o.Client, err = helper.GetManagementClient(o.BaseTestFrame.ClientInfo, orgID) + if err != nil { + return err + } + o.Admin, err = helper.GetAdminClient(o.BaseTestFrame.ClientInfo) + o.AsOrgDefaultDependency = strings.Replace(o.AsOrgDefaultDependency, o.OrgID, orgID, 1) + o.OrgID = orgID + return err +} + +func NewOrgTestFrame(t *testing.T, resourceType string) *OrgTestFrame { + ctx := context.Background() + cfg := acceptance.GetConfig().OrgLevel + baseFrame, err := NewBaseTestFrame(ctx, resourceType, cfg.Domain, cfg.AdminSAJSON) + if err != nil { + t.Fatalf("setting up test context failed: %v", err) + } + orgFrame := &OrgTestFrame{ + BaseTestFrame: *baseFrame, + } + if err = orgFrame.useOrgContext(""); err != nil { + t.Fatalf("setting up test context failed: %v", err) + } + org, err := orgFrame.GetOrgByDomainGlobal(baseFrame, &management.GetOrgByDomainGlobalRequest{Domain: "zitadel." + cfg.Domain}) + if err != nil { + t.Fatalf("failed to get org by domain: %v", err) + } + orgFrame.OrgID = org.GetOrg().GetId() + orgFrame.AsOrgDefaultDependency = fmt.Sprintf(` +data "zitadel_org" "default" { + id = "%s" +} +`, orgFrame.OrgID) + return orgFrame +} + +func (o OrgTestFrame) AnotherOrg(t *testing.T, name string) *OrgTestFrame { + org, err := o.Client.AddOrg(o, &management.AddOrgRequest{ + Name: name, + }) + if err != nil { + t.Fatalf("failed to create org: %v", err) + } + if err := o.useOrgContext(org.GetId()); err != nil { + t.Fatalf("failed to use org context: %v", err) + } + return &o +} diff --git a/zitadel/human_user/const.go b/zitadel/human_user/const.go new file mode 100644 index 00000000..5d73bce0 --- /dev/null +++ b/zitadel/human_user/const.go @@ -0,0 +1,27 @@ +package human_user + +const ( + UserIDVar = "user_id" + userStateVar = "state" + UserNameVar = "user_name" + loginNamesVar = "login_names" + preferredLoginNameVar = "preferred_login_name" + + firstNameVar = "first_name" + lastNameVar = "last_name" + nickNameVar = "nick_name" + DisplayNameVar = "display_name" + preferredLanguageVar = "preferred_language" + genderVar = "gender" + + isEmailVerifiedVar = "is_email_verified" + emailVar = "email" + + isPhoneVerifiedVar = "is_phone_verified" + phoneVar = "phone" + + InitialPasswordVar = "initial_password" + + defaultGenderString = "GENDER_UNSPECIFIED" + defaultPreferredLanguage = "und" +) diff --git a/zitadel/human_user/datasource.go b/zitadel/human_user/datasource.go new file mode 100644 index 00000000..8e774d55 --- /dev/null +++ b/zitadel/human_user/datasource.go @@ -0,0 +1,99 @@ +package human_user + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a human user situated under an organization, which then can be authorized through memberships or direct grants on other resources.", + Schema: map[string]*schema.Schema{ + UserIDVar: { + Type: schema.TypeString, + Required: true, + Description: "The ID of this resource.", + }, + helper.OrgIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the organization", + }, + userStateVar: { + Type: schema.TypeString, + Computed: true, + Description: "State of the user", + }, + UserNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Username", + }, + loginNamesVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "Loginnames", + }, + preferredLoginNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Preferred login name", + }, + firstNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "First name of the user", + }, + lastNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Last name of the user", + }, + nickNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Nick name of the user", + }, + DisplayNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Display name of the user", + }, + preferredLanguageVar: { + Type: schema.TypeString, + Description: "Preferred language of the user", + Computed: true, + }, + genderVar: { + Type: schema.TypeString, + Description: "Gender of the user", + Computed: true, + }, + emailVar: { + Type: schema.TypeString, + Computed: true, + Description: "Email of the user", + }, + isEmailVerifiedVar: { + Type: schema.TypeBool, + Computed: true, + Description: "Is the email verified of the user, can only be true if password of the user is set", + }, + phoneVar: { + Type: schema.TypeString, + Computed: true, + Description: "Phone of the user", + }, + isPhoneVerifiedVar: { + Type: schema.TypeBool, + Computed: true, + Description: "Is the phone verified of the user", + }, + }, + ReadContext: read, + } +} diff --git a/zitadel/human_user/funcs.go b/zitadel/human_user/funcs.go new file mode 100644 index 00000000..75b36606 --- /dev/null +++ b/zitadel/human_user/funcs.go @@ -0,0 +1,228 @@ +package human_user + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/user" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveUser(ctx, &management.RemoveUserRequest{ + Id: d.Id(), + }) + if err != nil { + return diag.Errorf("failed to delete user: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + firstName := d.Get(firstNameVar).(string) + lastName := d.Get(lastNameVar).(string) + addUser := &management.AddHumanUserRequest{ + UserName: d.Get(UserNameVar).(string), + Profile: &management.AddHumanUserRequest_Profile{ + FirstName: firstName, + LastName: lastName, + Gender: user.Gender(user.Gender_value[d.Get(genderVar).(string)]), + PreferredLanguage: d.Get(preferredLanguageVar).(string), + NickName: d.Get(nickNameVar).(string), + }, + InitialPassword: d.Get(InitialPasswordVar).(string), + } + + if displayname, ok := d.GetOk(DisplayNameVar); ok { + addUser.Profile.DisplayName = displayname.(string) + } else { + if err := d.Set(DisplayNameVar, defaultDisplayName(firstName, lastName)); err != nil { + return diag.Errorf("failed to set default display name for human user: %v", err) + } + } + + if email, ok := d.GetOk(emailVar); ok { + isVerified, isVerifiedOk := d.GetOk(isEmailVerifiedVar) + addUser.Email = &management.AddHumanUserRequest_Email{ + Email: email.(string), + IsEmailVerified: false, + } + if isVerifiedOk { + addUser.Email.IsEmailVerified = isVerified.(bool) + } + } + + if phone, ok := d.GetOk(phoneVar); ok { + isVerified, isVerifiedOk := d.GetOk(isPhoneVerifiedVar) + addUser.Phone = &management.AddHumanUserRequest_Phone{ + Phone: phone.(string), + IsPhoneVerified: false, + } + if isVerifiedOk { + addUser.Phone.IsPhoneVerified = isVerified.(bool) + } + } + + respUser, err := client.AddHumanUser(ctx, addUser) + if err != nil { + return diag.Errorf("failed to create human user: %v", err) + } + d.SetId(respUser.UserId) + // To avoid diffs for terraform plan -refresh=false right after creation, we query and set the computed values. + // The acceptance tests rely on this, too. + return read(ctx, d, m) +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChange(UserNameVar) { + _, err = client.UpdateUserName(ctx, &management.UpdateUserNameRequest{ + UserId: d.Id(), + UserName: d.Get(UserNameVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update username: %v", err) + } + } + + if d.HasChanges(firstNameVar, lastNameVar, nickNameVar, DisplayNameVar, preferredLanguageVar, genderVar) { + _, err := client.UpdateHumanProfile(ctx, &management.UpdateHumanProfileRequest{ + UserId: d.Id(), + FirstName: d.Get(firstNameVar).(string), + LastName: d.Get(lastNameVar).(string), + NickName: d.Get(nickNameVar).(string), + DisplayName: d.Get(DisplayNameVar).(string), + PreferredLanguage: d.Get(preferredLanguageVar).(string), + Gender: user.Gender(user.Gender_value[d.Get(genderVar).(string)]), + }) + if err != nil { + return diag.Errorf("failed to update human profile: %v", err) + } + } + + if d.HasChanges(emailVar, isEmailVerifiedVar) { + _, err = client.UpdateHumanEmail(ctx, &management.UpdateHumanEmailRequest{ + UserId: d.Id(), + Email: d.Get(emailVar).(string), + IsEmailVerified: d.Get(isEmailVerifiedVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to update human email: %v", err) + } + } + + if d.HasChanges(phoneVar, isPhoneVerifiedVar) { + _, err = client.UpdateHumanPhone(ctx, &management.UpdateHumanPhoneRequest{ + UserId: d.Id(), + Phone: d.Get(phoneVar).(string), + IsPhoneVerified: d.Get(isPhoneVerifiedVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to update human phone: %v", err) + } + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + respUser, err := client.GetUserByID(ctx, &management.GetUserByIDRequest{Id: helper.GetID(d, UserIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get user") + } + + user := respUser.GetUser() + set := map[string]interface{}{ + helper.OrgIDVar: user.GetDetails().GetResourceOwner(), + userStateVar: user.GetState().String(), + UserNameVar: user.GetUserName(), + loginNamesVar: user.GetLoginNames(), + preferredLoginNameVar: user.GetPreferredLoginName(), + } + + if human := user.GetHuman(); human != nil { + if profile := human.GetProfile(); profile != nil { + set[firstNameVar] = profile.GetFirstName() + set[lastNameVar] = profile.GetLastName() + set[DisplayNameVar] = profile.GetDisplayName() + set[nickNameVar] = profile.GetNickName() + set[preferredLanguageVar] = profile.GetPreferredLanguage() + if gender := profile.GetGender().String(); gender != "" { + set[genderVar] = gender + } + } + if email := human.GetEmail(); email != nil { + set[emailVar] = email.GetEmail() + set[isEmailVerifiedVar] = email.GetIsEmailVerified() + } + if phone := human.GetPhone(); phone != nil { + set[phoneVar] = phone.GetPhone() + set[isPhoneVerifiedVar] = phone.GetIsPhoneVerified() + } + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of user: %v", k, err) + } + } + d.SetId(user.GetId()) + return nil +} + +func defaultDisplayName(firstName, lastName string) string { + return firstName + " " + lastName +} diff --git a/zitadel/human_user/human_user_test_dep/dependency.go b/zitadel/human_user/human_user_test_dep/dependency.go new file mode 100644 index 00000000..b22a971e --- /dev/null +++ b/zitadel/human_user/human_user_test_dep/dependency.go @@ -0,0 +1,27 @@ +package human_user_test_dep + +import ( + "testing" + + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/human_user" +) + +func Create(t *testing.T, frame *test_utils.OrgTestFrame) (string, string) { + return test_utils.CreateDefaultDependency(t, "zitadel_human_user", human_user.UserIDVar, func() (string, error) { + user, err := frame.ImportHumanUser(frame, &management.ImportHumanUserRequest{ + UserName: frame.UniqueResourcesID, + Profile: &management.ImportHumanUserRequest_Profile{ + FirstName: "Don't", + LastName: "Care", + }, + Email: &management.ImportHumanUserRequest_Email{ + Email: "dont@care.com", + IsEmailVerified: true, + }, + }) + return user.GetUserId(), err + }) +} diff --git a/zitadel/human_user/resource.go b/zitadel/human_user/resource.go new file mode 100644 index 00000000..49a61033 --- /dev/null +++ b/zitadel/human_user/resource.go @@ -0,0 +1,146 @@ +package human_user + +import ( + "context" + + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/user" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a human user situated under an organization, which then can be authorized through memberships or direct grants on other resources.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + userStateVar: { + Type: schema.TypeString, + Computed: true, + Description: "State of the user", + /* Not necessary as long as only active users are created + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return EnumValueValidation(userStateVar, value.(string), user.UserState_value) + },*/ + }, + UserNameVar: { + Type: schema.TypeString, + Required: true, + Description: "Username", + }, + loginNamesVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "Loginnames", + ForceNew: true, + }, + preferredLoginNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Preferred login name", + ForceNew: true, + }, + + firstNameVar: { + Type: schema.TypeString, + Required: true, + Description: "First name of the user", + }, + lastNameVar: { + Type: schema.TypeString, + Required: true, + Description: "Last name of the user", + }, + nickNameVar: { + Type: schema.TypeString, + Optional: true, + Description: "Nick name of the user", + }, + DisplayNameVar: { + Type: schema.TypeString, + Optional: true, + Description: "Display name of the user", + Computed: true, + }, + preferredLanguageVar: { + Type: schema.TypeString, + Optional: true, + Description: "Preferred language of the user", + Default: defaultPreferredLanguage, + }, + genderVar: { + Type: schema.TypeString, + Optional: true, + Description: "Gender of the user" + helper.DescriptionEnumValuesList(user.Gender_name), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(genderVar, value.(string), user.Gender_value) + }, + Default: defaultGenderString, + }, + emailVar: { + Type: schema.TypeString, + Required: true, + Description: "Email of the user", + }, + isEmailVerifiedVar: { + Type: schema.TypeBool, + Optional: true, + Description: "Is the email verified of the user, can only be true if password of the user is set", + }, + phoneVar: { + Type: schema.TypeString, + Optional: true, + Description: "Phone of the user", + }, + isPhoneVerifiedVar: { + Type: schema.TypeBool, + Optional: true, + Description: "Is the phone verified of the user", + }, + InitialPasswordVar: { + Type: schema.TypeString, + Optional: true, + Description: "Initially set password for the user, not changeable after creation", + Sensitive: true, + ForceNew: true, + }, + }, + ReadContext: read, + CreateContext: create, + DeleteContext: delete, + UpdateContext: update, + CustomizeDiff: customdiff.All( + customdiff.IfValue(DisplayNameVar, func(ctx context.Context, value, meta interface{}) bool { + if value == "" { + return true + } + return false + }, func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error { + return diff.SetNew(DisplayNameVar, defaultDisplayName(diff.Get(firstNameVar).(string), diff.Get(lastNameVar).(string))) + }), + customdiff.IfValue(genderVar, func(ctx context.Context, value, meta interface{}) bool { + if value == "" { + return true + } + return false + }, func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error { + return diff.SetNew(genderVar, defaultGenderString) + }), + customdiff.IfValue(preferredLanguageVar, func(ctx context.Context, value, meta interface{}) bool { + if value == "" { + return true + } + return false + }, func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error { + return diff.SetNew(preferredLanguageVar, defaultPreferredLanguage) + }), + ), + Importer: helper.ImportWithIDAndOptionalOrgAndSecret(UserIDVar, InitialPasswordVar), + } +} diff --git a/zitadel/human_user/resource_test.go b/zitadel/human_user/resource_test.go new file mode 100644 index 00000000..7eb88793 --- /dev/null +++ b/zitadel/human_user/resource_test.go @@ -0,0 +1,57 @@ +package human_user_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/human_user" +) + +func TestAccHumanUser(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_human_user") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleUsername := test_utils.AttributeValue(t, human_user.UserNameVar, exampleAttributes).AsString() + resourceExample = strings.Replace(resourceExample, exampleUsername, frame.UniqueResourcesID, 1) + exampleProperty := test_utils.AttributeValue(t, human_user.DisplayNameVar, exampleAttributes).AsString() + updatedProperty := "updatedproperty" + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + false, + checkRemoteProperty(frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(frame), updatedProperty), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportOrgId(frame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, human_user.InitialPasswordVar), + ), + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetUserByID(frame, &management.GetUserByIDRequest{Id: frame.State(state).ID}) + if err != nil { + return err + } + actual := remoteResource.GetUser().GetHuman().GetProfile().GetDisplayName() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/idp_azure_ad/datasource.go b/zitadel/idp_azure_ad/datasource.go new file mode 100644 index 00000000..b771f848 --- /dev/null +++ b/zitadel/idp_azure_ad/datasource.go @@ -0,0 +1,28 @@ +package idp_azure_ad + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing an Azure AD IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + TenantTypeVar: TenantTypeDataSourceField, + TenantIDVar: TenantIDDataSourceField, + EmailVerifiedVar: EmailVerifiedDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/idp_azure_ad/funcs.go b/zitadel/idp_azure_ad/funcs.go new file mode 100644 index 00000000..c224ab35 --- /dev/null +++ b/zitadel/idp_azure_ad/funcs.go @@ -0,0 +1,134 @@ +package idp_azure_ad + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/idp" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + tenant, err := ConstructTenant(d) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddAzureADProvider(ctx, &admin.AddAzureADProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + Tenant: tenant, + EmailVerified: idp_utils.BoolValue(d, EmailVerifiedVar), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + tenant, err := ConstructTenant(d) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateAzureADProvider(ctx, &admin.UpdateAzureADProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + Tenant: tenant, + EmailVerified: idp_utils.BoolValue(d, EmailVerifiedVar), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &admin.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + respIdp := resp.GetIdp() + cfg := respIdp.GetConfig() + specificCfg := cfg.GetAzureAd() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + idp_utils.NameVar: respIdp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + EmailVerifiedVar: specificCfg.GetEmailVerified(), + TenantTypeVar: idp.AzureADTenantType_name[int32(specificCfg.GetTenant().GetTenantType())], + TenantIDVar: specificCfg.GetTenant().GetTenantId(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(respIdp.Id) + return nil +} + +func ConstructTenant(d *schema.ResourceData) (*idp.AzureADTenant, error) { + tenant := &idp.AzureADTenant{} + tenantId := idp_utils.StringValue(d, TenantIDVar) + tenantType := idp_utils.StringValue(d, TenantTypeVar) + if tenantId != "" && tenantType != "" { + return nil, fmt.Errorf("tenant_id and tenant_type are mutually exclusive, but got id %s and type %s", tenantId, tenantType) + } + if tenantId != "" { + tenant.Type = &idp.AzureADTenant_TenantId{ + TenantId: tenantId, + } + } else { + tenant.Type = &idp.AzureADTenant_TenantType{ + TenantType: idp.AzureADTenantType(idp.AzureADTenantType_value[tenantType]), + } + } + return tenant, nil +} diff --git a/zitadel/idp_azure_ad/idp_azure_ad_test_dep/dependency.go b/zitadel/idp_azure_ad/idp_azure_ad_test_dep/dependency.go new file mode 100644 index 00000000..4a8d15dd --- /dev/null +++ b/zitadel/idp_azure_ad/idp_azure_ad_test_dep/dependency.go @@ -0,0 +1,27 @@ +package idp_azure_ad_test_dep + +import ( + "testing" + + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/idp" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func Create(t *testing.T, frame test_utils.BaseTestFrame, client admin.AdminServiceClient) (string, string) { + return test_utils.CreateDefaultDependency(t, "zitadel_idp_azure_ad", idp_utils.IdpIDVar, func() (string, error) { + i, err := client.AddAzureADProvider(frame, &admin.AddAzureADProviderRequest{ + Name: "Azure AD " + frame.UniqueResourcesID, + Tenant: &idp.AzureADTenant{ + Type: &idp.AzureADTenant_TenantType{ + TenantType: idp.AzureADTenantType_AZURE_AD_TENANT_TYPE_COMMON, + }, + }, + ClientId: "dummy", + ClientSecret: "dummy", + }) + return i.GetId(), err + }) +} diff --git a/zitadel/idp_azure_ad/resource.go b/zitadel/idp_azure_ad/resource.go new file mode 100644 index 00000000..6bff3ede --- /dev/null +++ b/zitadel/idp_azure_ad/resource.go @@ -0,0 +1,32 @@ +package idp_azure_ad + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing an Azure AD IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + TenantTypeVar: TenantTypeResourceField, + TenantIDVar: TenantIDResourceField, + EmailVerifiedVar: EmailVerifiedResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/idp_azure_ad/resource_test.go b/zitadel/idp_azure_ad/resource_test.go new file mode 100644 index 00000000..6258d394 --- /dev/null +++ b/zitadel/idp_azure_ad/resource_test.go @@ -0,0 +1,12 @@ +package idp_azure_ad_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils/idp_test_utils" +) + +func TestAccInstanceIdPAzureAD(t *testing.T) { + idp_test_utils.RunInstanceIDPLifecyleTest(t, "zitadel_idp_azure_ad", idp_utils.ClientSecretVar) +} diff --git a/zitadel/idp_azure_ad/schema.go b/zitadel/idp_azure_ad/schema.go new file mode 100644 index 00000000..34c75e00 --- /dev/null +++ b/zitadel/idp_azure_ad/schema.go @@ -0,0 +1,55 @@ +package idp_azure_ad + +import ( + "fmt" + + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/idp" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + TenantTypeVar = "tenant_type" + TenantIDVar = "tenant_id" + EmailVerifiedVar = "email_verified" +) + +var ( + TenantTypeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: idp.AzureADTenantType_name[0], + Description: "the azure ad tenant type", + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(TenantTypeVar, value, idp.AzureADTenantType_value) + }, + } + TenantTypeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "the azure ad tenant type", + } + TenantIDResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: fmt.Sprintf("if %s is not set, the %s is used", TenantIDVar, TenantTypeVar), + } + TenantIDDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "the azure ad tenant id", + } + EmailVerifiedResourceField = &schema.Schema{ + Type: schema.TypeBool, + Required: true, + Description: "automatically mark emails as verified", + } + EmailVerifiedDataSourceField = &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + Description: "automatically mark emails as verified", + } +) diff --git a/zitadel/idp_github/datasource.go b/zitadel/idp_github/datasource.go new file mode 100644 index 00000000..2f491946 --- /dev/null +++ b/zitadel/idp_github/datasource.go @@ -0,0 +1,25 @@ +package idp_github + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a GitHub IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/idp_github/funcs.go b/zitadel/idp_github/funcs.go new file mode 100644 index 00000000..f0910b21 --- /dev/null +++ b/zitadel/idp_github/funcs.go @@ -0,0 +1,98 @@ +package idp_github + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddGitHubProvider(ctx, &admin.AddGitHubProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateGitHubProvider(ctx, &admin.UpdateGitHubProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &admin.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetGithub() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + idp_utils.NameVar: idp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/idp_github/resource.go b/zitadel/idp_github/resource.go new file mode 100644 index 00000000..b618664b --- /dev/null +++ b/zitadel/idp_github/resource.go @@ -0,0 +1,29 @@ +package idp_github + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a GitHub IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/idp_github/resource_test.go b/zitadel/idp_github/resource_test.go new file mode 100644 index 00000000..f4ab3df4 --- /dev/null +++ b/zitadel/idp_github/resource_test.go @@ -0,0 +1,12 @@ +package idp_github_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils/idp_test_utils" +) + +func TestAccInstanceIdPGitHub(t *testing.T) { + idp_test_utils.RunInstanceIDPLifecyleTest(t, "zitadel_idp_github", idp_utils.ClientSecretVar) +} diff --git a/zitadel/idp_github_es/datasource.go b/zitadel/idp_github_es/datasource.go new file mode 100644 index 00000000..21eab368 --- /dev/null +++ b/zitadel/idp_github_es/datasource.go @@ -0,0 +1,28 @@ +package idp_github_es + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a GitHub Enterprise IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + AuthorizationEndpointVar: AuthorizationEndpointDataSourceField, + TokenEndpointVar: TokenEndpointDataSourceField, + UserEndpointVar: UserEndpointDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/idp_github_es/funcs.go b/zitadel/idp_github_es/funcs.go new file mode 100644 index 00000000..150eb457 --- /dev/null +++ b/zitadel/idp_github_es/funcs.go @@ -0,0 +1,107 @@ +package idp_github_es + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddGitHubEnterpriseServerProvider(ctx, &admin.AddGitHubEnterpriseServerProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + AuthorizationEndpoint: idp_utils.StringValue(d, AuthorizationEndpointVar), + TokenEndpoint: idp_utils.StringValue(d, TokenEndpointVar), + UserEndpoint: idp_utils.StringValue(d, UserEndpointVar), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateGitHubEnterpriseServerProvider(ctx, &admin.UpdateGitHubEnterpriseServerProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + AuthorizationEndpoint: idp_utils.StringValue(d, AuthorizationEndpointVar), + TokenEndpoint: idp_utils.StringValue(d, TokenEndpointVar), + UserEndpoint: idp_utils.StringValue(d, UserEndpointVar), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &admin.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetGithubEs() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + idp_utils.NameVar: idp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + AuthorizationEndpointVar: specificCfg.GetAuthorizationEndpoint(), + TokenEndpointVar: specificCfg.GetTokenEndpoint(), + UserEndpointVar: specificCfg.GetUserEndpoint(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/idp_github_es/resource.go b/zitadel/idp_github_es/resource.go new file mode 100644 index 00000000..5f7a3de5 --- /dev/null +++ b/zitadel/idp_github_es/resource.go @@ -0,0 +1,32 @@ +package idp_github_es + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a GitHub Enterprise IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + AuthorizationEndpointVar: AuthorizationEndpointResourceField, + TokenEndpointVar: TokenEndpointResourceField, + UserEndpointVar: UserEndpointResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/idp_github_es/resource_test.go b/zitadel/idp_github_es/resource_test.go new file mode 100644 index 00000000..aa03d45b --- /dev/null +++ b/zitadel/idp_github_es/resource_test.go @@ -0,0 +1,12 @@ +package idp_github_es_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils/idp_test_utils" +) + +func TestAccInstanceIdPGitHubES(t *testing.T) { + idp_test_utils.RunInstanceIDPLifecyleTest(t, "zitadel_idp_github_es", idp_utils.ClientSecretVar) +} diff --git a/zitadel/idp_github_es/schema.go b/zitadel/idp_github_es/schema.go new file mode 100644 index 00000000..b7d7067f --- /dev/null +++ b/zitadel/idp_github_es/schema.go @@ -0,0 +1,42 @@ +package idp_github_es + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +const ( + AuthorizationEndpointVar = "authorization_endpoint" + TokenEndpointVar = "token_endpoint" + UserEndpointVar = "user_endpoint" +) + +var ( + AuthorizationEndpointResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "the providers authorization endpoint", + } + AuthorizationEndpointDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "the providers authorization endpoint", + } + TokenEndpointResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "the providers token endpoint", + } + TokenEndpointDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "the providers token endpoint", + } + UserEndpointResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "the providers user endpoint", + } + UserEndpointDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "the providers user endpoint", + } +) diff --git a/zitadel/idp_gitlab/datasource.go b/zitadel/idp_gitlab/datasource.go new file mode 100644 index 00000000..4dff408d --- /dev/null +++ b/zitadel/idp_gitlab/datasource.go @@ -0,0 +1,25 @@ +package idp_gitlab + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a GitLab IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/idp_gitlab/funcs.go b/zitadel/idp_gitlab/funcs.go new file mode 100644 index 00000000..7c094e6c --- /dev/null +++ b/zitadel/idp_gitlab/funcs.go @@ -0,0 +1,98 @@ +package idp_gitlab + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddGitLabProvider(ctx, &admin.AddGitLabProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateGitLabProvider(ctx, &admin.UpdateGitLabProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &admin.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetGitlab() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + idp_utils.NameVar: idp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/idp_gitlab/resource.go b/zitadel/idp_gitlab/resource.go new file mode 100644 index 00000000..8142f15f --- /dev/null +++ b/zitadel/idp_gitlab/resource.go @@ -0,0 +1,29 @@ +package idp_gitlab + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a GitLab IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/idp_gitlab/resource_test.go b/zitadel/idp_gitlab/resource_test.go new file mode 100644 index 00000000..2b21c635 --- /dev/null +++ b/zitadel/idp_gitlab/resource_test.go @@ -0,0 +1,12 @@ +package idp_gitlab_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils/idp_test_utils" +) + +func TestAccInstanceIdPGitLab(t *testing.T) { + idp_test_utils.RunInstanceIDPLifecyleTest(t, "zitadel_idp_gitlab", idp_utils.ClientSecretVar) +} diff --git a/zitadel/idp_gitlab_self_hosted/datasource.go b/zitadel/idp_gitlab_self_hosted/datasource.go new file mode 100644 index 00000000..4f5cf4cf --- /dev/null +++ b/zitadel/idp_gitlab_self_hosted/datasource.go @@ -0,0 +1,26 @@ +package idp_gitlab_self_hosted + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a GitLab Self Hosted IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + IssuerVar: IssuerDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/idp_gitlab_self_hosted/funcs.go b/zitadel/idp_gitlab_self_hosted/funcs.go new file mode 100644 index 00000000..3049f590 --- /dev/null +++ b/zitadel/idp_gitlab_self_hosted/funcs.go @@ -0,0 +1,101 @@ +package idp_gitlab_self_hosted + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddGitLabSelfHostedProvider(ctx, &admin.AddGitLabSelfHostedProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + Issuer: idp_utils.StringValue(d, IssuerVar), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateGitLabSelfHostedProvider(ctx, &admin.UpdateGitLabSelfHostedProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + Issuer: idp_utils.StringValue(d, IssuerVar), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &admin.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetGitlabSelfHosted() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + idp_utils.NameVar: idp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + IssuerVar: specificCfg.GetIssuer(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/idp_gitlab_self_hosted/resource.go b/zitadel/idp_gitlab_self_hosted/resource.go new file mode 100644 index 00000000..ff8ec632 --- /dev/null +++ b/zitadel/idp_gitlab_self_hosted/resource.go @@ -0,0 +1,30 @@ +package idp_gitlab_self_hosted + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a GitLab Self Hosted IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + IssuerVar: IssuerResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/idp_gitlab_self_hosted/resource_test.go b/zitadel/idp_gitlab_self_hosted/resource_test.go new file mode 100644 index 00000000..2f547c03 --- /dev/null +++ b/zitadel/idp_gitlab_self_hosted/resource_test.go @@ -0,0 +1,12 @@ +package idp_gitlab_self_hosted_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils/idp_test_utils" +) + +func TestAccInstanceIdPGitLabSelfHosted(t *testing.T) { + idp_test_utils.RunInstanceIDPLifecyleTest(t, "zitadel_idp_gitlab_self_hosted", idp_utils.ClientSecretVar) +} diff --git a/zitadel/idp_gitlab_self_hosted/schema.go b/zitadel/idp_gitlab_self_hosted/schema.go new file mode 100644 index 00000000..3b3d571d --- /dev/null +++ b/zitadel/idp_gitlab_self_hosted/schema.go @@ -0,0 +1,18 @@ +package idp_gitlab_self_hosted + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +const IssuerVar = "issuer" + +var ( + IssuerResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "the providers issuer", + } + IssuerDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "the providers issuer", + } +) diff --git a/zitadel/idp_google/datasource.go b/zitadel/idp_google/datasource.go new file mode 100644 index 00000000..8c88a694 --- /dev/null +++ b/zitadel/idp_google/datasource.go @@ -0,0 +1,25 @@ +package idp_google + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a Google IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/idp_google/funcs.go b/zitadel/idp_google/funcs.go new file mode 100644 index 00000000..07d5d581 --- /dev/null +++ b/zitadel/idp_google/funcs.go @@ -0,0 +1,98 @@ +package idp_google + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddGoogleProvider(ctx, &admin.AddGoogleProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateGoogleProvider(ctx, &admin.UpdateGoogleProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &admin.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetGoogle() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + idp_utils.NameVar: idp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/idp_google/idp_google_test_dep/dependency.go b/zitadel/idp_google/idp_google_test_dep/dependency.go new file mode 100644 index 00000000..d52cc4bb --- /dev/null +++ b/zitadel/idp_google/idp_google_test_dep/dependency.go @@ -0,0 +1,21 @@ +package idp_google_test_dep + +import ( + "testing" + + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func Create(t *testing.T, frame test_utils.BaseTestFrame, client admin.AdminServiceClient) (string, string) { + return test_utils.CreateDefaultDependency(t, "zitadel_idp_google", idp_utils.IdpIDVar, func() (string, error) { + i, err := client.AddGoogleProvider(frame, &admin.AddGoogleProviderRequest{ + Name: "Google " + frame.UniqueResourcesID, + ClientId: "dummy", + ClientSecret: "dummy", + }) + return i.GetId(), err + }) +} diff --git a/zitadel/idp_google/resource.go b/zitadel/idp_google/resource.go new file mode 100644 index 00000000..a888a724 --- /dev/null +++ b/zitadel/idp_google/resource.go @@ -0,0 +1,29 @@ +package idp_google + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a Google IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/idp_google/resource_test.go b/zitadel/idp_google/resource_test.go new file mode 100644 index 00000000..9930612a --- /dev/null +++ b/zitadel/idp_google/resource_test.go @@ -0,0 +1,12 @@ +package idp_google_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils/idp_test_utils" +) + +func TestAccInstanceIdPGoogle(t *testing.T) { + idp_test_utils.RunInstanceIDPLifecyleTest(t, "zitadel_idp_google", idp_utils.ClientSecretVar) +} diff --git a/zitadel/idp_ldap/datasource.go b/zitadel/idp_ldap/datasource.go new file mode 100644 index 00000000..2be14e26 --- /dev/null +++ b/zitadel/idp_ldap/datasource.go @@ -0,0 +1,46 @@ +package idp_ldap + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing an LDAP IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + + ServersVar: ServersDataSourceField, + StartTLSVar: StartTLSDataSourceField, + BaseDNVar: BaseDNDataSourceField, + BindDNVar: BindDNDataSourceField, + BindPasswordVar: BindPasswordDataSourceField, + UserBaseVar: UserBaseDataSourceField, + UserObjectClassesVar: UserObjectClassesDataSourceField, + UserFiltersVar: UserFiltersDataSourceField, + TimeoutVar: TimeoutDataSourceField, + IdAttributeVar: IdAttributeDataSourceField, + + FirstNameAttributeVar: FirstNameAttributeDataSourceField, + LastNameAttributeVar: LastNameAttributeDataSourceField, + DisplayNameAttributeVar: DisplayNameAttributeDataSourceField, + NickNameAttributeVar: NickNameAttributeDataSourceField, + PreferredUsernameAttributeVar: PreferredUsernameAttributeDataSourceField, + EmailAttributeVar: EmailAttributeDataSourceField, + EmailVerifiedAttributeVar: EmailVerifiedAttributeDataSourceField, + PhoneAttributeVar: PhoneAttributeDataSourceField, + PhoneVerifiedAttributeVar: PhoneVerifiedAttributeDataSourceField, + PreferredLanguageAttributeVar: PreferredLanguageAttributeDataSourceField, + AvatarURLAttributeVar: AvatarURLAttributeDataSourceField, + ProfileAttributeVar: ProfileAttributeDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/idp_ldap/funcs.go b/zitadel/idp_ldap/funcs.go new file mode 100644 index 00000000..7c7f4617 --- /dev/null +++ b/zitadel/idp_ldap/funcs.go @@ -0,0 +1,178 @@ +package idp_ldap + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/idp" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + timeout, err := time.ParseDuration(idp_utils.StringValue(d, TimeoutVar)) + if err != nil { + return diag.FromErr(err) + } + req := &admin.AddLDAPProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + + Servers: idp_utils.InterfaceToStringSlice(d.Get(ServersVar)), + StartTls: idp_utils.BoolValue(d, StartTLSVar), + BaseDn: idp_utils.StringValue(d, BaseDNVar), + BindDn: idp_utils.StringValue(d, BindDNVar), + BindPassword: idp_utils.StringValue(d, BindPasswordVar), + UserBase: idp_utils.StringValue(d, UserBaseVar), + UserObjectClasses: helper.GetOkSetToStringSlice(d, UserObjectClassesVar), + UserFilters: helper.GetOkSetToStringSlice(d, UserFiltersVar), + Timeout: durationpb.New(timeout), + + Attributes: &idp.LDAPAttributes{ + IdAttribute: idp_utils.StringValue(d, IdAttributeVar), + FirstNameAttribute: idp_utils.StringValue(d, FirstNameAttributeVar), + LastNameAttribute: idp_utils.StringValue(d, LastNameAttributeVar), + DisplayNameAttribute: idp_utils.StringValue(d, DisplayNameAttributeVar), + NickNameAttribute: idp_utils.StringValue(d, NickNameAttributeVar), + PreferredUsernameAttribute: idp_utils.StringValue(d, PreferredUsernameAttributeVar), + EmailAttribute: idp_utils.StringValue(d, EmailAttributeVar), + EmailVerifiedAttribute: idp_utils.StringValue(d, EmailVerifiedAttributeVar), + PhoneAttribute: idp_utils.StringValue(d, PhoneAttributeVar), + PhoneVerifiedAttribute: idp_utils.StringValue(d, PhoneVerifiedAttributeVar), + PreferredLanguageAttribute: idp_utils.StringValue(d, PreferredLanguageAttributeVar), + AvatarUrlAttribute: idp_utils.StringValue(d, AvatarURLAttributeVar), + ProfileAttribute: idp_utils.StringValue(d, ProfileAttributeVar), + }, + } + resp, err := client.AddLDAPProvider(ctx, req) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + timeout, err := time.ParseDuration(idp_utils.StringValue(d, TimeoutVar)) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateLDAPProvider(ctx, &admin.UpdateLDAPProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + + Servers: idp_utils.InterfaceToStringSlice(d.Get(ServersVar)), + StartTls: idp_utils.BoolValue(d, StartTLSVar), + BaseDn: idp_utils.StringValue(d, BaseDNVar), + BindDn: idp_utils.StringValue(d, BindDNVar), + BindPassword: idp_utils.StringValue(d, BindPasswordVar), + UserBase: idp_utils.StringValue(d, UserBaseVar), + UserObjectClasses: helper.GetOkSetToStringSlice(d, UserObjectClassesVar), + UserFilters: helper.GetOkSetToStringSlice(d, UserFiltersVar), + Timeout: durationpb.New(timeout), + + Attributes: &idp.LDAPAttributes{ + IdAttribute: idp_utils.StringValue(d, IdAttributeVar), + FirstNameAttribute: idp_utils.StringValue(d, FirstNameAttributeVar), + LastNameAttribute: idp_utils.StringValue(d, LastNameAttributeVar), + DisplayNameAttribute: idp_utils.StringValue(d, DisplayNameAttributeVar), + NickNameAttribute: idp_utils.StringValue(d, NickNameAttributeVar), + PreferredUsernameAttribute: idp_utils.StringValue(d, PreferredUsernameAttributeVar), + EmailAttribute: idp_utils.StringValue(d, EmailAttributeVar), + EmailVerifiedAttribute: idp_utils.StringValue(d, EmailVerifiedAttributeVar), + PhoneAttribute: idp_utils.StringValue(d, PhoneAttributeVar), + PhoneVerifiedAttribute: idp_utils.StringValue(d, PhoneVerifiedAttributeVar), + PreferredLanguageAttribute: idp_utils.StringValue(d, PreferredLanguageAttributeVar), + AvatarUrlAttribute: idp_utils.StringValue(d, AvatarURLAttributeVar), + ProfileAttribute: idp_utils.StringValue(d, ProfileAttributeVar), + }, + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &admin.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetLdap() + attributesCfg := specificCfg.GetAttributes() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + idp_utils.NameVar: idp.GetName(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + + ServersVar: specificCfg.GetServers(), + StartTLSVar: specificCfg.GetStartTls(), + BaseDNVar: specificCfg.GetBaseDn(), + BindDNVar: specificCfg.GetBindDn(), + BindPasswordVar: idp_utils.StringValue(d, BindPasswordVar), + UserBaseVar: specificCfg.GetUserBase(), + UserObjectClassesVar: specificCfg.GetUserObjectClasses(), + UserFiltersVar: specificCfg.GetUserFilters(), + TimeoutVar: specificCfg.GetTimeout().AsDuration().String(), + IdAttributeVar: attributesCfg.GetIdAttribute(), + + FirstNameAttributeVar: attributesCfg.GetFirstNameAttribute(), + LastNameAttributeVar: attributesCfg.GetLastNameAttribute(), + DisplayNameAttributeVar: attributesCfg.GetDisplayNameAttribute(), + NickNameAttributeVar: attributesCfg.GetNickNameAttribute(), + PreferredUsernameAttributeVar: attributesCfg.GetPreferredUsernameAttribute(), + EmailAttributeVar: attributesCfg.GetEmailAttribute(), + EmailVerifiedAttributeVar: attributesCfg.GetEmailVerifiedAttribute(), + PhoneAttributeVar: attributesCfg.GetPhoneAttribute(), + PhoneVerifiedAttributeVar: attributesCfg.GetPhoneVerifiedAttribute(), + PreferredLanguageAttributeVar: attributesCfg.GetPreferredLanguageAttribute(), + AvatarURLAttributeVar: attributesCfg.GetAvatarUrlAttribute(), + ProfileAttributeVar: attributesCfg.GetProfileAttribute(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/idp_ldap/resource.go b/zitadel/idp_ldap/resource.go new file mode 100644 index 00000000..911e0133 --- /dev/null +++ b/zitadel/idp_ldap/resource.go @@ -0,0 +1,50 @@ +package idp_ldap + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing an LDAP IDP on the instance.", + Schema: map[string]*schema.Schema{ + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + + ServersVar: ServersResourceField, + StartTLSVar: StartTLSResourceField, + BaseDNVar: BaseDNResourceField, + BindDNVar: BindDNResourceField, + BindPasswordVar: BindPasswordResourceField, + UserBaseVar: UserBaseResourceField, + UserObjectClassesVar: UserObjectClassesResourceField, + UserFiltersVar: UserFiltersResourceField, + TimeoutVar: TimeoutResourceField, + IdAttributeVar: IdAttributeResourceField, + + FirstNameAttributeVar: FirstNameAttributeResourceField, + LastNameAttributeVar: LastNameAttributeResourceField, + DisplayNameAttributeVar: DisplayNameAttributeResourceField, + NickNameAttributeVar: NickNameAttributeResourceField, + PreferredUsernameAttributeVar: PreferredUsernameAttributeResourceField, + EmailAttributeVar: EmailAttributeResourceField, + EmailVerifiedAttributeVar: EmailVerifiedAttributeResourceField, + PhoneAttributeVar: PhoneAttributeResourceField, + PhoneVerifiedAttributeVar: PhoneVerifiedAttributeResourceField, + PreferredLanguageAttributeVar: PreferredLanguageAttributeResourceField, + AvatarURLAttributeVar: AvatarURLAttributeResourceField, + ProfileAttributeVar: ProfileAttributeResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalSecret(idp_utils.IdpIDVar, BindPasswordVar), + } +} diff --git a/zitadel/idp_ldap/resource_test.go b/zitadel/idp_ldap/resource_test.go new file mode 100644 index 00000000..10afb269 --- /dev/null +++ b/zitadel/idp_ldap/resource_test.go @@ -0,0 +1,12 @@ +package idp_ldap_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_ldap" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils/idp_test_utils" +) + +func TestAccInstanceIdPLDAP(t *testing.T) { + idp_test_utils.RunInstanceIDPLifecyleTest(t, "zitadel_idp_ldap", idp_ldap.BindPasswordVar) +} diff --git a/zitadel/idp_ldap/schema.go b/zitadel/idp_ldap/schema.go new file mode 100644 index 00000000..0342fa18 --- /dev/null +++ b/zitadel/idp_ldap/schema.go @@ -0,0 +1,273 @@ +package idp_ldap + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +const ( + ServersVar = "servers" + StartTLSVar = "start_tls" + BaseDNVar = "base_dn" + BindDNVar = "bind_dn" + BindPasswordVar = "bind_password" + UserBaseVar = "user_base" + UserObjectClassesVar = "user_object_classes" + UserFiltersVar = "user_filters" + TimeoutVar = "timeout" + IdAttributeVar = "id_attribute" + + FirstNameAttributeVar = "first_name_attribute" + LastNameAttributeVar = "last_name_attribute" + DisplayNameAttributeVar = "display_name_attribute" + NickNameAttributeVar = "nick_name_attribute" + PreferredUsernameAttributeVar = "preferred_username_attribute" + EmailAttributeVar = "email_attribute" + EmailVerifiedAttributeVar = "email_verified_attribute" + PhoneAttributeVar = "phone_attribute" + PhoneVerifiedAttributeVar = "phone_verified_attribute" + PreferredLanguageAttributeVar = "preferred_language_attribute" + AvatarURLAttributeVar = "avatar_url_attribute" + ProfileAttributeVar = "profile_attribute" +) + +var ( + ServersResourceField = &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + Description: "Servers to try in order for establishing LDAP connections", + } + ServersDataSourceField = &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "Servers to try in order for establishing LDAP connections", + } + StartTLSResourceField = &schema.Schema{ + Type: schema.TypeBool, + Required: true, + Description: "Wether to use StartTLS for LDAP connections", + } + StartTLSDataSourceField = &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + Description: "Wether to use StartTLS for LDAP connections", + } + BaseDNResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "Base DN for LDAP connections", + } + BaseDNDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "Base DN for LDAP connections", + } + BindDNResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "Bind DN for LDAP connections", + } + BindDNDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "Bind DN for LDAP connections", + } + BindPasswordResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "Bind password for LDAP connections", + Sensitive: true, + } + BindPasswordDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "Bind password for LDAP connections", + Sensitive: true, + } + UserBaseResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "User base for LDAP connections", + } + UserBaseDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User base for LDAP connections", + } + UserObjectClassesResourceField = &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + Description: "User object classes for LDAP connections", + } + UserObjectClassesDataSourceField = &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "User object classes for LDAP connections", + } + UserFiltersResourceField = &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + Description: "User filters for LDAP connections", + } + UserFiltersDataSourceField = &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "User filters for LDAP connections", + } + TimeoutResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "Timeout for LDAP connections", + } + TimeoutDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "Timeout for LDAP connections", + } + IdAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the id", + } + IdAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the id", + } + + FirstNameAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the first name", + } + FirstNameAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the first name", + } + LastNameAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the last name", + } + LastNameAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the last name", + } + DisplayNameAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the display name", + } + DisplayNameAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the display name", + } + NickNameAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the nick name", + } + NickNameAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the nick name", + } + PreferredUsernameAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the preferred username", + } + PreferredUsernameAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the preferred username", + } + EmailAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the email", + } + EmailAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the email", + } + EmailVerifiedAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the email verified state", + } + EmailVerifiedAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the email verified state", + } + PhoneAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the phone", + } + PhoneAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the phone", + } + PhoneVerifiedAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the phone verified state", + } + PhoneVerifiedAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the phone verified state", + } + PreferredLanguageAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the preferred language", + } + PreferredLanguageAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the preferred language", + } + AvatarURLAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the avatar url", + } + AvatarURLAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the avatar url", + } + ProfileAttributeResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "User attribute for the profile", + } + ProfileAttributeDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "User attribute for the profile", + } +) diff --git a/zitadel/idp_utils/funcs.go b/zitadel/idp_utils/funcs.go new file mode 100644 index 00000000..e1d732f9 --- /dev/null +++ b/zitadel/idp_utils/funcs.go @@ -0,0 +1,58 @@ +package idp_utils + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/idp" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func Delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + _, err = client.DeleteProvider(ctx, &admin.DeleteProviderRequest{Id: d.Id()}) + if err != nil { + return diag.Errorf("failed to delete idp: %v", err) + } + return nil +} + +func StringValue(d *schema.ResourceData, attributeVar string) string { + return d.Get(attributeVar).(string) +} + +func BoolValue(d *schema.ResourceData, attributeVar string) bool { + return d.Get(attributeVar).(bool) +} + +func ScopesValue(d *schema.ResourceData) []string { + return helper.GetOkSetToStringSlice(d, ScopesVar) +} + +func ProviderOptionsValue(d *schema.ResourceData) *idp.Options { + return &idp.Options{ + IsLinkingAllowed: BoolValue(d, IsLinkingAllowedVar), + IsCreationAllowed: BoolValue(d, IsCreationAllowedVar), + IsAutoUpdate: BoolValue(d, IsAutoUpdateVar), + IsAutoCreation: BoolValue(d, IsAutoCreationVar), + } +} + +func InterfaceToStringSlice(in interface{}) []string { + slice := in.([]interface{}) + ret := make([]string, 0) + for _, item := range slice { + ret = append(ret, item.(string)) + } + return ret +} diff --git a/zitadel/idp_utils/idp_test_utils/checks.go b/zitadel/idp_utils/idp_test_utils/checks.go new file mode 100644 index 00000000..89766c39 --- /dev/null +++ b/zitadel/idp_utils/idp_test_utils/checks.go @@ -0,0 +1,39 @@ +package idp_test_utils + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func CheckCreationAllowed(frame test_utils.InstanceTestFrame) func(bool) resource.TestCheckFunc { + return func(expectAllowed bool) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteProvider, err := frame.Client.GetProviderByID(frame, &admin.GetProviderByIDRequest{Id: frame.State(state).ID}) + if err != nil { + return err + } + actual := remoteProvider.GetIdp().GetConfig().GetOptions().GetIsCreationAllowed() + if actual != expectAllowed { + return fmt.Errorf("expected creation allowed to be %t, but got %t", expectAllowed, actual) + } + return nil + } + } +} + +func CheckDestroy(frame test_utils.InstanceTestFrame) resource.TestCheckFunc { + return func(state *terraform.State) error { + err := CheckCreationAllowed(frame)(true)(state) + if status.Code(err) != codes.NotFound { + return fmt.Errorf("expected not found error but got: %w", err) + } + return nil + } +} diff --git a/zitadel/idp_utils/idp_test_utils/lifecyletest.go b/zitadel/idp_utils/idp_test_utils/lifecyletest.go new file mode 100644 index 00000000..78703447 --- /dev/null +++ b/zitadel/idp_utils/idp_test_utils/lifecyletest.go @@ -0,0 +1,36 @@ +package idp_test_utils + +import ( + "strings" + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func RunInstanceIDPLifecyleTest(t *testing.T, resourceName, secretAttribute string) { + frame := test_utils.NewInstanceTestFrame(t, resourceName) + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + nameProperty := test_utils.AttributeValue(t, idp_utils.NameVar, exampleAttributes).AsString() + // Using a unique name makes the test idempotent on failures + resourceExample = strings.Replace(resourceExample, nameProperty, frame.UniqueResourcesID, 1) + exampleProperty := test_utils.AttributeValue(t, idp_utils.IsCreationAllowedVar, exampleAttributes).True() + exampleSecret := test_utils.AttributeValue(t, secretAttribute, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, exampleSecret), + true, false, + secretAttribute, exampleSecret, "an_updated_secret", + false, + CheckCreationAllowed(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + CheckDestroy(*frame), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, secretAttribute), + ), + ) +} diff --git a/zitadel/idp_utils/schema.go b/zitadel/idp_utils/schema.go new file mode 100644 index 00000000..9be0294b --- /dev/null +++ b/zitadel/idp_utils/schema.go @@ -0,0 +1,111 @@ +package idp_utils + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +const ( + IdpIDVar = "id" + NameVar = "name" + ClientIDVar = "client_id" + ClientSecretVar = "client_secret" + ScopesVar = "scopes" + IsLinkingAllowedVar = "is_linking_allowed" + IsCreationAllowedVar = "is_creation_allowed" + IsAutoCreationVar = "is_auto_creation" + IsAutoUpdateVar = "is_auto_update" +) + +var ( + IdPIDDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "The ID of this resource.", + } + NameResourceField = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "Name of the IDP", + } + NameDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "Name of the IDP", + } + ClientIDResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "client id generated by the identity provider", + } + ClientIDDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "client id generated by the identity provider", + } + ClientSecretResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "client secret generated by the identity provider", + Sensitive: true, + } + ClientSecretDataSourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "client secret generated by the identity provider", + Sensitive: true, + } + ScopesResourceField = &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "the scopes requested by ZITADEL during the request on the identity provider", + } + ScopesDataSourceField = &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "the scopes requested by ZITADEL during the request on the identity provider", + } + IsLinkingAllowedResourceField = &schema.Schema{ + Type: schema.TypeBool, + Required: true, + Description: "enable if users should be able to link an existing ZITADEL user with an external account", + } + IsLinkingAllowedDataSourceField = &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + Description: "enabled if users are able to link an existing ZITADEL user with an external account", + } + IsCreationAllowedResourceField = &schema.Schema{ + Type: schema.TypeBool, + Required: true, + Description: "enable if users should be able to create a new account in ZITADEL when using an external account", + } + IsCreationAllowedDataSourceField = &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + Description: "enabled if users are able to create a new account in ZITADEL when using an external account", + } + IsAutoCreationResourceField = &schema.Schema{ + Type: schema.TypeBool, + Required: true, + Description: "enable if a new account in ZITADEL should be created automatically on login with an external account", + } + IsAutoCreationDataSourceField = &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + Description: "enabled if a new account in ZITADEL are created automatically on login with an external account", + } + IsAutoUpdateResourceField = &schema.Schema{ + Type: schema.TypeBool, + Required: true, + Description: "enable if a the ZITADEL account fields should be updated automatically on each login", + } + IsAutoUpdateDataSourceField = &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + Description: "enabled if a the ZITADEL account fields are updated automatically on each login", + } +) diff --git a/zitadel/init_message_text/resource.go b/zitadel/init_message_text/resource.go new file mode 100644 index 00000000..ee290ecf --- /dev/null +++ b/zitadel/init_message_text/resource.go @@ -0,0 +1,251 @@ +package init_message_text + +import ( + "context" + "strings" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &initMessageTextResource{} +) + +func New() resource.Resource { + return &initMessageTextResource{} +} + +type initMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *initMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_init_message_text" +} + +func (r *initMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return text.GenSchemaMessageCustomText(ctx) +} + +func (r *initMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *initMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomInitMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomInitMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *initMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + orgID, language := getID(ctx, state) + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomInitMessageText(ctx, &management.GetCustomInitMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *initMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomInitMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomInitMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *initMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + orgID, language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomInitMessageTextToDefault(ctx, &management.ResetCustomInitMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, orgID string, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(orgID + "_" + language) + attrs[helper.OrgIDVar] = types.StringValue(orgID) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) (string, string) { + id := helper.GetStringFromAttr(ctx, obj.Attributes(), "id") + parts := strings.Split(id, "_") + if len(parts) == 2 { + return parts[0], parts[1] + } + return helper.GetStringFromAttr(ctx, obj.Attributes(), helper.OrgIDVar), helper.GetStringFromAttr(ctx, obj.Attributes(), LanguageVar) +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(plan.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(state.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} diff --git a/zitadel/init_message_text/resource_test.go b/zitadel/init_message_text/resource_test.go new file mode 100644 index 00000000..b74f4ad1 --- /dev/null +++ b/zitadel/init_message_text/resource_test.go @@ -0,0 +1,51 @@ +package init_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/init_message_text" +) + +func TestAccInitMessageText(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_init_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + exampleLanguage := test_utils.AttributeValue(t, init_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtitle", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^\d{18}_%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("Zitadel - Initialize User"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomInitMessageText(frame, &management.GetCustomInitMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/instance_member/const.go b/zitadel/instance_member/const.go new file mode 100644 index 00000000..325a92c8 --- /dev/null +++ b/zitadel/instance_member/const.go @@ -0,0 +1,6 @@ +package instance_member + +const ( + UserIDVar = "user_id" + RolesVar = "roles" +) diff --git a/zitadel/instance_member/funcs.go b/zitadel/instance_member/funcs.go new file mode 100644 index 00000000..02d3a784 --- /dev/null +++ b/zitadel/instance_member/funcs.go @@ -0,0 +1,143 @@ +package instance_member + +import ( + "context" + "strings" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/member" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveIAMMember(ctx, &admin.RemoveIAMMemberRequest{ + UserId: d.Get(UserIDVar).(string), + }) + if err != nil { + return diag.Errorf("failed to delete instance member: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateIAMMember(ctx, &admin.UpdateIAMMemberRequest{ + UserId: d.Get(UserIDVar).(string), + Roles: helper.GetOkSetToStringSlice(d, RolesVar), + }) + if err != nil { + return diag.Errorf("failed to update instance member: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + userID := d.Get(UserIDVar).(string) + resp, err := client.AddIAMMember(ctx, &admin.AddIAMMemberRequest{ + UserId: userID, + Roles: helper.GetOkSetToStringSlice(d, RolesVar), + }) + if err != nil { + return diag.Errorf("failed to create instance member: %v", err) + } + d.SetId(getInstanceMemberID(resp.GetDetails().GetResourceOwner(), userID)) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + userID := d.Get(UserIDVar).(string) + resp, err := client.ListIAMMembers(ctx, &admin.ListIAMMembersRequest{ + Queries: []*member.SearchQuery{{ + Query: &member.SearchQuery_UserIdQuery{ + UserIdQuery: &member.UserIDQuery{ + UserId: userID, + }, + }, + }}, + }) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to list instance members") + } + + if len(resp.Result) == 1 { + member := resp.Result[0] + set := map[string]interface{}{ + UserIDVar: member.GetUserId(), + RolesVar: member.GetRoles(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of instance member: %v", k, err) + } + } + d.SetId(getInstanceMemberID(member.GetDetails().GetResourceOwner(), userID)) + return nil + } + + d.SetId("") + return nil +} + +func getInstanceMemberID(instance string, userID string) string { + return instance + "_" + userID +} + +func splitInstanceMemberID(memberID string) (string, string) { + parts := strings.Split(memberID, "_") + return parts[0], parts[1] +} diff --git a/zitadel/instance_member/resource.go b/zitadel/instance_member/resource.go new file mode 100644 index 00000000..44fa26d5 --- /dev/null +++ b/zitadel/instance_member/resource.go @@ -0,0 +1,34 @@ +package instance_member + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the membership of a user on an instance, defined with the given role.", + Schema: map[string]*schema.Schema{ + UserIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the user", + ForceNew: true, + }, + RolesVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + Description: "List of roles granted, full list available here: https://zitadel.com/docs/guides/manage/console/managers#roles", + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithEmptyID(helper.NewImportAttribute(UserIDVar, helper.ConvertID, false)), + } +} diff --git a/zitadel/instance_member/resource_test.go b/zitadel/instance_member/resource_test.go new file mode 100644 index 00000000..c816b06f --- /dev/null +++ b/zitadel/instance_member/resource_test.go @@ -0,0 +1,60 @@ +package instance_member_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/member" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/human_user/human_user_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/instance_member" +) + +func TestAccInstanceMember(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_instance_member") + userDep, userID := human_user_test_dep.Create(t, frame) + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, instance_member.RolesVar, exampleAttributes).AsValueSlice()[0].AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, userDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "IAM_OWNER_VIEWER", + "", "", "", + true, + checkRemoteProperty(*frame, userID), + regexp.MustCompile(fmt.Sprintf("^%s_%s$", helper.ZitadelGeneratedIdPattern, helper.ZitadelGeneratedIdPattern)), + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(*frame, userID), ""), + test_utils.ImportStateAttribute(frame.BaseTestFrame, instance_member.UserIDVar), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame, userID string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.Admin.ListIAMMembers(frame, &admin.ListIAMMembersRequest{ + Queries: []*member.SearchQuery{{ + Query: &member.SearchQuery_UserIdQuery{UserIdQuery: &member.UserIDQuery{UserId: userID}}, + }}, + }) + if err != nil { + return err + } + if len(resp.Result) == 0 || len(resp.Result[0].Roles) == 0 { + return fmt.Errorf("expected 1 user with 1 role, but got %d: %w", len(resp.Result), test_utils.ErrNotFound) + } + actual := resp.Result[0].Roles[0] + if expect != actual { + return fmt.Errorf("expected role %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/label_policy/const.go b/zitadel/label_policy/const.go new file mode 100644 index 00000000..5de7cafb --- /dev/null +++ b/zitadel/label_policy/const.go @@ -0,0 +1,40 @@ +package label_policy + +const ( + primaryColorVar = "primary_color" + hideLoginNameSuffixVar = "hide_login_name_suffix" + warnColorVar = "warn_color" + backgroundColorVar = "background_color" + fontColorVar = "font_color" + primaryColorDarkVar = "primary_color_dark" + backgroundColorDarkVar = "background_color_dark" + warnColorDarkVar = "warn_color_dark" + fontColorDarkVar = "font_color_dark" + disableWatermarkVar = "disable_watermark" + LogoPathVar = "logo_path" + LogoHashVar = "logo_hash" + logoURLVar = "logo_url" + IconPathVar = "icon_path" + IconHashVar = "icon_hash" + iconURLVar = "icon_url" + LogoDarkPathVar = "logo_dark_path" + LogoDarkHashVar = "logo_dark_hash" + logoURLDarkVar = "logo_url_dark" + IconDarkPathVar = "icon_dark_path" + IconDarkHashVar = "icon_dark_hash" + iconURLDarkVar = "icon_url_dark" + FontPathVar = "font_path" + FontHashVar = "font_hash" + fontURLVar = "font_url" + SetActiveVar = "set_active" +) + +const ( + assetAPI = "/assets/v1" + labelPolicyURL = "/org/policy/label" + logoURL = assetAPI + labelPolicyURL + "/logo" + logoDarkURL = logoURL + "/dark" + iconURL = assetAPI + labelPolicyURL + "/icon" + iconDarkURL = iconURL + "/dark" + fontURL = assetAPI + labelPolicyURL + "/font" +) diff --git a/zitadel/label_policy/funcs.go b/zitadel/label_policy/funcs.go new file mode 100644 index 00000000..09d8f346 --- /dev/null +++ b/zitadel/label_policy/funcs.go @@ -0,0 +1,251 @@ +package label_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.ResetLabelPolicyToDefault(ctx, &management.ResetLabelPolicyToDefaultRequest{}) + if err != nil { + return diag.Errorf("failed to reset label policy: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChanges( + primaryColorVar, + hideLoginNameSuffixVar, + warnColorVar, + backgroundColorVar, + fontColorVar, + primaryColorDarkVar, + backgroundColorDarkVar, + warnColorDarkVar, + fontColorDarkVar, + disableWatermarkVar, + ) { + resp, err := client.UpdateCustomLabelPolicy(ctx, &management.UpdateCustomLabelPolicyRequest{ + PrimaryColor: d.Get(primaryColorVar).(string), + HideLoginNameSuffix: d.Get(hideLoginNameSuffixVar).(bool), + WarnColor: d.Get(warnColorVar).(string), + BackgroundColor: d.Get(backgroundColorVar).(string), + FontColor: d.Get(fontColorVar).(string), + PrimaryColorDark: d.Get(primaryColorDarkVar).(string), + BackgroundColorDark: d.Get(backgroundColorDarkVar).(string), + WarnColorDark: d.Get(warnColorDarkVar).(string), + FontColorDark: d.Get(fontColorDarkVar).(string), + DisableWatermark: d.Get(disableWatermarkVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to update label policy: %v", err) + } + d.SetId(resp.Details.ResourceOwner) + } + + if d.HasChanges(LogoHashVar, LogoPathVar) { + if err := helper.OrgFormFilePost(clientinfo, logoURL, d.Get(LogoPathVar).(string), org); err != nil { + return diag.Errorf("failed to upload logo: %v", err) + } + } + if d.HasChanges(LogoDarkHashVar, LogoDarkPathVar) { + if err := helper.OrgFormFilePost(clientinfo, logoDarkURL, d.Get(LogoDarkPathVar).(string), org); err != nil { + return diag.Errorf("failed to upload logo dark: %v", err) + } + } + if d.HasChanges(IconHashVar, IconPathVar) { + if err := helper.OrgFormFilePost(clientinfo, iconURL, d.Get(IconPathVar).(string), org); err != nil { + return diag.Errorf("failed to upload icon: %v", err) + } + } + if d.HasChanges(IconDarkHashVar, IconDarkPathVar) { + if err := helper.OrgFormFilePost(clientinfo, iconDarkURL, d.Get(IconDarkPathVar).(string), org); err != nil { + return diag.Errorf("failed to upload icon dark: %v", err) + } + } + if d.HasChanges(FontHashVar, FontPathVar) { + if err := helper.OrgFormFilePost(clientinfo, fontURL, d.Get(FontPathVar).(string), org); err != nil { + return diag.Errorf("failed to upload font: %v", err) + } + } + + if d.HasChanges( + primaryColorVar, + hideLoginNameSuffixVar, + warnColorVar, + backgroundColorVar, + fontColorVar, + primaryColorDarkVar, + backgroundColorDarkVar, + warnColorDarkVar, + fontColorDarkVar, + disableWatermarkVar, + LogoHashVar, + LogoDarkHashVar, + IconHashVar, + IconDarkHashVar, + FontHashVar, + ) { + if d.Get(SetActiveVar).(bool) { + if _, err := client.ActivateCustomLabelPolicy(ctx, &management.ActivateCustomLabelPolicyRequest{}); err != nil { + return diag.Errorf("failed to activate label policy: %v", err) + } + } + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.AddCustomLabelPolicy(ctx, &management.AddCustomLabelPolicyRequest{ + PrimaryColor: d.Get(primaryColorVar).(string), + HideLoginNameSuffix: d.Get(hideLoginNameSuffixVar).(bool), + WarnColor: d.Get(warnColorVar).(string), + BackgroundColor: d.Get(backgroundColorVar).(string), + FontColor: d.Get(fontColorVar).(string), + PrimaryColorDark: d.Get(primaryColorDarkVar).(string), + BackgroundColorDark: d.Get(backgroundColorDarkVar).(string), + WarnColorDark: d.Get(warnColorDarkVar).(string), + FontColorDark: d.Get(fontColorDarkVar).(string), + DisableWatermark: d.Get(disableWatermarkVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to create label policy: %v", err) + } + d.SetId(org) + + if d.Get(LogoHashVar) != "" && d.Get(LogoPathVar) != "" { + if err := helper.OrgFormFilePost(clientinfo, logoURL, d.Get(LogoPathVar).(string), org); err != nil { + return diag.Errorf("failed to upload logo: %v", err) + } + } + if d.Get(LogoDarkHashVar) != "" && d.Get(LogoDarkPathVar) != "" { + if err := helper.OrgFormFilePost(clientinfo, logoDarkURL, d.Get(LogoDarkPathVar).(string), org); err != nil { + return diag.Errorf("failed to upload logo dark: %v", err) + } + } + if d.Get(IconHashVar) != "" && d.Get(IconPathVar) != "" { + if err := helper.OrgFormFilePost(clientinfo, iconURL, d.Get(IconPathVar).(string), org); err != nil { + return diag.Errorf("failed to upload icon: %v", err) + } + } + if d.Get(IconDarkHashVar) != "" && d.Get(IconDarkPathVar) != "" { + if err := helper.OrgFormFilePost(clientinfo, iconDarkURL, d.Get(IconDarkPathVar).(string), org); err != nil { + return diag.Errorf("failed to upload icon dark: %v", err) + } + } + if d.Get(FontHashVar) != "" && d.Get(FontPathVar) != "" { + if err := helper.OrgFormFilePost(clientinfo, fontURL, d.Get(FontPathVar).(string), org); err != nil { + return diag.Errorf("failed to upload font: %v", err) + } + } + + if d.Get(SetActiveVar).(bool) { + if _, err := client.ActivateCustomLabelPolicy(ctx, &management.ActivateCustomLabelPolicyRequest{}); err != nil { + return diag.Errorf("failed to activate label policy: %v", err) + } + } + + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetPreviewLabelPolicy(ctx, &management.GetPreviewLabelPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get label policy") + } + + policy := resp.Policy + if policy.GetIsDefault() == true { + d.SetId("") + return nil + } + set := map[string]interface{}{ + helper.OrgIDVar: policy.GetDetails().GetResourceOwner(), + primaryColorVar: policy.GetPrimaryColor(), + hideLoginNameSuffixVar: policy.GetHideLoginNameSuffix(), + warnColorVar: policy.GetWarnColor(), + backgroundColorVar: policy.GetBackgroundColor(), + fontColorVar: policy.GetFontColor(), + primaryColorDarkVar: policy.GetPrimaryColorDark(), + backgroundColorDarkVar: policy.GetBackgroundColorDark(), + warnColorDarkVar: policy.GetWarnColorDark(), + fontColorDarkVar: policy.GetFontColorDark(), + disableWatermarkVar: policy.GetDisableWatermark(), + logoURLVar: policy.GetLogoUrl(), + iconURLVar: policy.GetIconUrl(), + logoURLDarkVar: policy.GetLogoUrlDark(), + iconURLDarkVar: policy.GetIconUrlDark(), + fontURLVar: policy.GetFontUrl(), + } + + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of label policy: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/label_policy/resource.go b/zitadel/label_policy/resource.go new file mode 100644 index 00000000..cefcc592 --- /dev/null +++ b/zitadel/label_policy/resource.go @@ -0,0 +1,151 @@ +package label_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the custom label policy of an organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + primaryColorVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for primary color", + }, + hideLoginNameSuffixVar: { + Type: schema.TypeBool, + Required: true, + Description: "hides the org suffix on the login form if the scope \"urn:zitadel:iam:org:domain:primary:{domainname}\" is set. Details about this scope in https://zitadel.com/docs/apis/openidoauth/scopes#reserved-scopes", + }, + warnColorVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for warn color", + }, + backgroundColorVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for background color", + }, + fontColorVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for font color", + }, + primaryColorDarkVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for primary color dark theme", + }, + backgroundColorDarkVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for background color dark theme", + }, + warnColorDarkVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for warn color dark theme", + }, + fontColorDarkVar: { + Type: schema.TypeString, + Required: true, + Description: "hex value for font color dark theme", + }, + disableWatermarkVar: { + Type: schema.TypeBool, + Required: true, + Description: "disable watermark", + }, + LogoPathVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + LogoHashVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + logoURLVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + IconPathVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + IconHashVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + iconURLVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + LogoDarkPathVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + LogoDarkHashVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + logoURLDarkVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + IconDarkPathVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + IconDarkHashVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + iconURLDarkVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + FontPathVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + FontHashVar: { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + fontURLVar: { + Type: schema.TypeString, + Computed: true, + Description: "", + }, + SetActiveVar: { + Type: schema.TypeBool, + Optional: true, + Description: "set the label policy active after creating/updating", + }, + }, + ReadContext: read, + CreateContext: create, + DeleteContext: delete, + UpdateContext: update, + Importer: helper.ImportWithOptionalOrg(), + } +} diff --git a/zitadel/label_policy/resource_test.go b/zitadel/label_policy/resource_test.go new file mode 100644 index 00000000..13ab2297 --- /dev/null +++ b/zitadel/label_policy/resource_test.go @@ -0,0 +1,102 @@ +package label_policy_test + +import ( + "encoding/base64" + "fmt" + "io" + "os" + "regexp" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/label_policy" +) + +func TestAccLabelPolicy(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_label_policy") + testSVGFile := writeFile(t, strings.NewReader(testSVG)) + defer testSVGFile.Close() + defer os.Remove(testSVGFile.Name()) + testFontFile := writeFile(t, base64.NewDecoder(base64.StdEncoding, strings.NewReader(testFontBase64))) + defer testFontFile.Close() + defer os.Remove(testFontFile.Name()) + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + resourceExample = regexp.MustCompile("/path/to/[a-zA-Z_]+\\.jpg").ReplaceAllString(resourceExample, testSVGFile.Name()) + resourceExample = regexp.MustCompile("/path/to/[a-zA-Z_]+\\.tff").ReplaceAllString(resourceExample, testFontFile.Name()) + exampleProperty := test_utils.AttributeValue(t, "primary_color", exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "#5469d3", + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + checkRemoteProperty(*frame)(exampleProperty), + test_utils.ImportOrgId(frame), + label_policy.SetActiveVar, + label_policy.LogoHashVar, + label_policy.LogoPathVar, + label_policy.LogoDarkHashVar, + label_policy.LogoDarkPathVar, + label_policy.IconHashVar, + label_policy.IconPathVar, + label_policy.IconDarkHashVar, + label_policy.IconDarkPathVar, + label_policy.FontHashVar, + label_policy.FontPathVar, + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetLabelPolicy(frame, &management.GetLabelPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetPrimaryColor() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} + +func writeFile(t *testing.T, content io.Reader) *os.File { + file, err := os.CreateTemp("", "TestAccDefaultLabelPolicy") + if err != nil { + t.Fatalf("creating temp file failed: %v", err) + } + if _, err := io.Copy(file, content); err != nil { + t.Fatalf("writing temp file failed: %v", err) + } + return file +} + +const ( + testSVG = ` + + + +` + testFontBase64 = ` +AAEAAAAHAEAAAgAwY21hcAAJAHYAAAEAAAAALGdseWbxy2aYAAABNAAAAFxoZWFk8jXd+AAAAHwA +AAA2aGhlYQZhAMoAAAC0AAAAJGhtdHgEdABqAAAA+AAAAAhsb2NhAC4AFAAAASwAAAAGbWF4cAAF +AAsAAADYAAAAIAABAAAAAQAA9ZwpRF8PPPUAAgPoAAAAALSS9AAAAAAA3C+mXAAGAAACWAK8AAAA +AwACAAAAAAAAAAEAAAQA/nAAAAJYAAb//wJYAAEAAAAAAAAAAAAAAAAAAAACAAEAAAACAAsAAgAA +AAAAAAAAAAAAAAAAAAAAAAAAAAACWABkAhwABgAAAAEAAAADAAAADAAEACAAAAAEAAQAAQAAAEH/ +/wAAAEH////AAAEAAAAAAAAAFAAuAAAAAgBkAAACWAK8AAMABwAAMxEhESUhESFkAfT+NAGk/lwC +vP1EKAJsAAIABgAAAh0CkAACAAoAABMzAwETMxMjJyMHrcRj/vjaYN1ZPu9CAQsBQP21ApD9cMjI +AA== +` +) diff --git a/zitadel/lockout_policy/const.go b/zitadel/lockout_policy/const.go new file mode 100644 index 00000000..e2b070a3 --- /dev/null +++ b/zitadel/lockout_policy/const.go @@ -0,0 +1,5 @@ +package lockout_policy + +const ( + maxPasswordAttemptsVar = "max_password_attempts" +) diff --git a/zitadel/lockout_policy/funcs.go b/zitadel/lockout_policy/funcs.go new file mode 100644 index 00000000..a214d5db --- /dev/null +++ b/zitadel/lockout_policy/funcs.go @@ -0,0 +1,108 @@ +package lockout_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + _, err = client.ResetLockoutPolicyToDefault(ctx, &management.ResetLockoutPolicyToDefaultRequest{}) + if err != nil { + return diag.Errorf("failed to reset lockout policy: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateCustomLockoutPolicy(ctx, &management.UpdateCustomLockoutPolicyRequest{ + MaxPasswordAttempts: uint32(d.Get(maxPasswordAttemptsVar).(int)), + }) + if err != nil { + return diag.Errorf("failed to update lockout policy: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + _, err = client.AddCustomLockoutPolicy(ctx, &management.AddCustomLockoutPolicyRequest{ + MaxPasswordAttempts: uint32(d.Get(maxPasswordAttemptsVar).(int)), + }) + if err != nil { + return diag.Errorf("failed to create lockout policy: %v", err) + } + d.SetId(org) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetLockoutPolicy(ctx, &management.GetLockoutPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get lockout policy") + } + policy := resp.Policy + if policy.GetIsDefault() == true { + d.SetId("") + return nil + } + set := map[string]interface{}{ + helper.OrgIDVar: policy.GetDetails().GetResourceOwner(), + maxPasswordAttemptsVar: policy.GetMaxPasswordAttempts(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of lockout policy: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/lockout_policy/resource.go b/zitadel/lockout_policy/resource.go new file mode 100644 index 00000000..26c1e3f6 --- /dev/null +++ b/zitadel/lockout_policy/resource.go @@ -0,0 +1,26 @@ +package lockout_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the custom lockout policy of an organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + maxPasswordAttemptsVar: { + Type: schema.TypeInt, + Required: true, + Description: "Maximum password check attempts before the account gets locked. Attempts are reset as soon as the password is entered correct or the password is reset.", + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithOptionalOrg(), + } +} diff --git a/zitadel/lockout_policy/resource_test.go b/zitadel/lockout_policy/resource_test.go new file mode 100644 index 00000000..e4a3546f --- /dev/null +++ b/zitadel/lockout_policy/resource_test.go @@ -0,0 +1,52 @@ +package lockout_policy_test + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccLockoutPolicy(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_lockout_policy") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty, err := strconv.ParseUint(test_utils.AttributeValue(t, "max_password_attempts", exampleAttributes).AsString(), 10, 64) + if err != nil { + t.Fatalf("could not parse example property: %v", err) + } + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, 10, + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + checkRemoteProperty(*frame)(0), + test_utils.ImportOrgId(frame), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame) func(uint64) resource.TestCheckFunc { + return func(expect uint64) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetLockoutPolicy(frame, &management.GetLockoutPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetMaxPasswordAttempts() + if actual != expect { + return fmt.Errorf("expected %d, but got %d", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/login_policy/const.go b/zitadel/login_policy/const.go new file mode 100644 index 00000000..e4575875 --- /dev/null +++ b/zitadel/login_policy/const.go @@ -0,0 +1,24 @@ +package login_policy + +const ( + allowUsernamePasswordVar = "user_login" + allowRegisterVar = "allow_register" + allowExternalIDPVar = "allow_external_idp" + forceMFAVar = "force_mfa" + forceMFALocalOnlyVar = "force_mfa_local_only" + passwordlessTypeVar = "passwordless_type" + hidePasswordResetVar = "hide_password_reset" + passwordCheckLifetimeVar = "password_check_lifetime" + externalLoginCheckLifetimeVar = "external_login_check_lifetime" + mfaInitSkipLifetimeVar = "mfa_init_skip_lifetime" + secondFactorCheckLifetimeVar = "second_factor_check_lifetime" + multiFactorCheckLifetimeVar = "multi_factor_check_lifetime" + ignoreUnknownUsernamesVar = "ignore_unknown_usernames" + DefaultRedirectURIVar = "default_redirect_uri" + secondFactorsVar = "second_factors" + multiFactorsVar = "multi_factors" + idpsVar = "idps" + allowDomainDiscovery = "allow_domain_discovery" + disableLoginWithEmail = "disable_login_with_email" + disableLoginWithPhone = "disable_login_with_phone" +) diff --git a/zitadel/login_policy/funcs.go b/zitadel/login_policy/funcs.go new file mode 100644 index 00000000..c431e7a0 --- /dev/null +++ b/zitadel/login_policy/funcs.go @@ -0,0 +1,371 @@ +package login_policy + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + mgmtclient "github.com/zitadel/zitadel-go/v2/pkg/client/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/idp" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/policy" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + _, err = client.ResetLoginPolicyToDefault(ctx, &management.ResetLoginPolicyToDefaultRequest{}) + if err != nil { + return diag.Errorf("failed to reset login policy: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + if d.HasChanges( + allowUsernamePasswordVar, + allowRegisterVar, + allowExternalIDPVar, + forceMFAVar, + forceMFALocalOnlyVar, + passwordlessTypeVar, + hidePasswordResetVar, + ignoreUnknownUsernamesVar, + DefaultRedirectURIVar, + passwordCheckLifetimeVar, + externalLoginCheckLifetimeVar, + mfaInitSkipLifetimeVar, + secondFactorCheckLifetimeVar, + multiFactorCheckLifetimeVar, + allowDomainDiscovery, + disableLoginWithEmail, + disableLoginWithPhone, + ) { + passwordCheckLT, err := time.ParseDuration(d.Get(passwordCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + externalLoginCheckLT, err := time.ParseDuration(d.Get(externalLoginCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + mfaInitSkipLT, err := time.ParseDuration(d.Get(mfaInitSkipLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + secondFactorCheckLT, err := time.ParseDuration(d.Get(secondFactorCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + multiFactorCheckLT, err := time.ParseDuration(d.Get(multiFactorCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateCustomLoginPolicy(ctx, &management.UpdateCustomLoginPolicyRequest{ + AllowUsernamePassword: d.Get(allowUsernamePasswordVar).(bool), + AllowRegister: d.Get(allowRegisterVar).(bool), + AllowExternalIdp: d.Get(allowExternalIDPVar).(bool), + ForceMfa: d.Get(forceMFAVar).(bool), + PasswordlessType: policy.PasswordlessType(policy.PasswordlessType_value[d.Get(passwordlessTypeVar).(string)]), + HidePasswordReset: d.Get(hidePasswordResetVar).(bool), + IgnoreUnknownUsernames: d.Get(ignoreUnknownUsernamesVar).(bool), + DefaultRedirectUri: d.Get(DefaultRedirectURIVar).(string), + PasswordCheckLifetime: durationpb.New(passwordCheckLT), + ExternalLoginCheckLifetime: durationpb.New(externalLoginCheckLT), + MfaInitSkipLifetime: durationpb.New(mfaInitSkipLT), + SecondFactorCheckLifetime: durationpb.New(secondFactorCheckLT), + MultiFactorCheckLifetime: durationpb.New(multiFactorCheckLT), + AllowDomainDiscovery: d.Get(allowDomainDiscovery).(bool), + DisableLoginWithEmail: d.Get(disableLoginWithEmail).(bool), + DisableLoginWithPhone: d.Get(disableLoginWithPhone).(bool), + ForceMfaLocalOnly: d.Get(forceMFALocalOnlyVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to update login policy: %v", err) + } + } + + if d.HasChange(secondFactorsVar) { + o, n := d.GetChange(secondFactorsVar) + addSecondFactor, deleteSecondFactors := helper.GetAddAndDelete(helper.SetToStringSlice(o.(*schema.Set)), helper.SetToStringSlice(n.(*schema.Set))) + + for _, factor := range addSecondFactor { + if _, err := client.AddSecondFactorToLoginPolicy(ctx, &management.AddSecondFactorToLoginPolicyRequest{ + Type: policy.SecondFactorType(policy.SecondFactorType_value[factor]), + }); err != nil { + return diag.FromErr(err) + } + } + for _, factor := range deleteSecondFactors { + if _, err := client.RemoveSecondFactorFromLoginPolicy(ctx, &management.RemoveSecondFactorFromLoginPolicyRequest{ + Type: policy.SecondFactorType(policy.SecondFactorType_value[factor]), + }); err != nil { + return diag.FromErr(err) + } + } + } + + if d.HasChange(multiFactorsVar) { + o, n := d.GetChange(multiFactorsVar) + addMultiFactor, deleteMultiFactors := helper.GetAddAndDelete(helper.SetToStringSlice(o.(*schema.Set)), helper.SetToStringSlice(n.(*schema.Set))) + + for _, factor := range addMultiFactor { + if _, err := client.AddMultiFactorToLoginPolicy(ctx, &management.AddMultiFactorToLoginPolicyRequest{ + Type: policy.MultiFactorType(policy.MultiFactorType_value[factor]), + }); err != nil { + return diag.FromErr(err) + } + } + for _, factor := range deleteMultiFactors { + if _, err := client.RemoveMultiFactorFromLoginPolicy(ctx, &management.RemoveMultiFactorFromLoginPolicyRequest{ + Type: policy.MultiFactorType(policy.MultiFactorType_value[factor]), + }); err != nil { + return diag.FromErr(err) + } + } + } + + if d.HasChange(idpsVar) { + o, n := d.GetChange(idpsVar) + addIdps, deleteIdps := helper.GetAddAndDelete(helper.SetToStringSlice(o.(*schema.Set)), helper.SetToStringSlice(n.(*schema.Set))) + + for _, addIdp := range addIdps { + idpOwnerType, err := getIDPOwnerType(ctx, client, addIdp) + if err != nil { + return diag.FromErr(err) + } + if _, err := client.AddIDPToLoginPolicy(ctx, &management.AddIDPToLoginPolicyRequest{IdpId: addIdp, OwnerType: idpOwnerType}); err != nil { + return diag.FromErr(err) + } + } + for _, deleteIdp := range deleteIdps { + if _, err := client.RemoveIDPFromLoginPolicy(ctx, &management.RemoveIDPFromLoginPolicyRequest{IdpId: deleteIdp}); err != nil { + return diag.FromErr(err) + } + } + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + passwordCheckLT, err := time.ParseDuration(d.Get(passwordCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + externalLoginCheckLT, err := time.ParseDuration(d.Get(externalLoginCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + mfaInitSkipLT, err := time.ParseDuration(d.Get(mfaInitSkipLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + secondFactorCheckLT, err := time.ParseDuration(d.Get(secondFactorCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + multiFactorCheckLT, err := time.ParseDuration(d.Get(multiFactorCheckLifetimeVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + secondFactors := make([]policy.SecondFactorType, 0) + if secondFactorsSet, ok := d.GetOk(secondFactorsVar); ok { + for _, factor := range secondFactorsSet.(*schema.Set).List() { + secondFactors = append(secondFactors, policy.SecondFactorType(policy.SecondFactorType_value[factor.(string)])) + } + } + multiFactors := make([]policy.MultiFactorType, 0) + if multiFactorsSet, ok := d.GetOk(multiFactorsVar); ok { + for _, factor := range multiFactorsSet.(*schema.Set).List() { + multiFactors = append(multiFactors, policy.MultiFactorType(policy.MultiFactorType_value[factor.(string)])) + } + } + + _, err = client.AddCustomLoginPolicy(ctx, &management.AddCustomLoginPolicyRequest{ + AllowUsernamePassword: d.Get(allowUsernamePasswordVar).(bool), + AllowRegister: d.Get(allowRegisterVar).(bool), + AllowExternalIdp: d.Get(allowExternalIDPVar).(bool), + ForceMfa: d.Get(forceMFAVar).(bool), + PasswordlessType: policy.PasswordlessType(policy.PasswordlessType_value[d.Get(passwordlessTypeVar).(string)]), + HidePasswordReset: d.Get(hidePasswordResetVar).(bool), + IgnoreUnknownUsernames: d.Get(ignoreUnknownUsernamesVar).(bool), + DefaultRedirectUri: d.Get(DefaultRedirectURIVar).(string), + PasswordCheckLifetime: durationpb.New(passwordCheckLT), + ExternalLoginCheckLifetime: durationpb.New(externalLoginCheckLT), + MfaInitSkipLifetime: durationpb.New(mfaInitSkipLT), + SecondFactorCheckLifetime: durationpb.New(secondFactorCheckLT), + MultiFactorCheckLifetime: durationpb.New(multiFactorCheckLT), + SecondFactors: secondFactors, + MultiFactors: multiFactors, + AllowDomainDiscovery: d.Get(allowDomainDiscovery).(bool), + DisableLoginWithEmail: d.Get(disableLoginWithEmail).(bool), + DisableLoginWithPhone: d.Get(disableLoginWithPhone).(bool), + ForceMfaLocalOnly: d.Get(forceMFALocalOnlyVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to create login policy: %v", err) + } + d.SetId(org) + + idps := helper.GetOkSetToStringSlice(d, idpsVar) + for _, addIdp := range idps { + idpOwnerType, err := getIDPOwnerType(ctx, client, addIdp) + if err != nil { + return diag.FromErr(err) + } + if _, err := client.AddIDPToLoginPolicy(ctx, &management.AddIDPToLoginPolicyRequest{IdpId: addIdp, OwnerType: idpOwnerType}); err != nil { + return diag.FromErr(err) + } + } + return nil +} + +func getIDPOwnerType(ctx context.Context, client *mgmtclient.Client, id string) (idp.IDPOwnerType, error) { + ownertype := idp.IDPOwnerType_IDP_OWNER_TYPE_UNSPECIFIED + templateProvider, err := client.GetProviderByID(ctx, &management.GetProviderByIDRequest{Id: id}) + err = helper.IgnoreIfNotFoundError(err) + if err != nil { + return idp.IDPOwnerType_IDP_OWNER_TYPE_UNSPECIFIED, err + } + ownertype = templateProvider.GetIdp().GetOwner() + if ownertype == idp.IDPOwnerType_IDP_OWNER_TYPE_UNSPECIFIED { + // Maybe it's a legacy provider + legacyProvider, err := client.GetOrgIDPByID(ctx, &management.GetOrgIDPByIDRequest{Id: id}) + err = helper.IgnoreIfNotFoundError(err) + if err != nil { + return idp.IDPOwnerType_IDP_OWNER_TYPE_UNSPECIFIED, err + } + ownertype = legacyProvider.GetIdp().GetOwner() + } + + if ownertype == idp.IDPOwnerType_IDP_OWNER_TYPE_UNSPECIFIED { + // If the IDP is still not found, the only possibility left is a legacy instance level provider + ownertype = idp.IDPOwnerType_IDP_OWNER_TYPE_SYSTEM + } + return ownertype, nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetLoginPolicy(ctx, &management.GetLoginPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get login policy") + } + policy := resp.Policy + if policy.GetIsDefault() == true { + d.SetId("") + return nil + } + set := map[string]interface{}{ + helper.OrgIDVar: policy.GetDetails().GetResourceOwner(), + allowUsernamePasswordVar: policy.GetAllowUsernamePassword(), + allowRegisterVar: policy.GetAllowRegister(), + allowExternalIDPVar: policy.GetAllowExternalIdp(), + forceMFAVar: policy.GetForceMfa(), + passwordlessTypeVar: policy.GetPasswordlessType().String(), + hidePasswordResetVar: policy.GetHidePasswordReset(), + ignoreUnknownUsernamesVar: policy.GetIgnoreUnknownUsernames(), + DefaultRedirectURIVar: policy.GetDefaultRedirectUri(), + passwordCheckLifetimeVar: policy.GetPasswordCheckLifetime().AsDuration().String(), + externalLoginCheckLifetimeVar: policy.GetExternalLoginCheckLifetime().AsDuration().String(), + mfaInitSkipLifetimeVar: policy.GetMfaInitSkipLifetime().AsDuration().String(), + secondFactorCheckLifetimeVar: policy.GetSecondFactorCheckLifetime().AsDuration().String(), + multiFactorCheckLifetimeVar: policy.GetMultiFactorCheckLifetime().AsDuration().String(), + allowDomainDiscovery: policy.GetAllowDomainDiscovery(), + disableLoginWithEmail: policy.GetDisableLoginWithEmail(), + disableLoginWithPhone: policy.GetDisableLoginWithPhone(), + forceMFALocalOnlyVar: policy.GetForceMfaLocalOnly(), + } + respSecond, err := client.ListLoginPolicySecondFactors(ctx, &management.ListLoginPolicySecondFactorsRequest{}) + if err != nil { + return diag.Errorf("failed to get login policy secondfactors: %v", err) + } + if len(respSecond.GetResult()) > 0 { + factors := make([]string, 0) + for _, item := range respSecond.GetResult() { + factors = append(factors, item.String()) + } + set[secondFactorsVar] = factors + } + respMulti, err := client.ListLoginPolicyMultiFactors(ctx, &management.ListLoginPolicyMultiFactorsRequest{}) + if err != nil { + return diag.Errorf("failed to get login policy multifactors: %v", err) + } + if len(respMulti.GetResult()) > 0 { + factors := make([]string, 0) + for _, item := range respMulti.GetResult() { + factors = append(factors, item.String()) + } + set[multiFactorsVar] = factors + } + respIDPs, err := client.ListLoginPolicyIDPs(ctx, &management.ListLoginPolicyIDPsRequest{}) + if err != nil { + return diag.Errorf("failed to get login policy idps: %v", err) + } + if len(respIDPs.GetResult()) > 0 { + idps := make([]string, 0) + for _, idpItem := range respIDPs.GetResult() { + idps = append(idps, idpItem.IdpId) + } + set[idpsVar] = idps + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of login policy: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/login_policy/resource.go b/zitadel/login_policy/resource.go new file mode 100644 index 00000000..aeb7bb38 --- /dev/null +++ b/zitadel/login_policy/resource.go @@ -0,0 +1,130 @@ +package login_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the custom login policy of an organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + allowUsernamePasswordVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if a user is allowed to login with his username and password", + }, + allowRegisterVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if a person is allowed to register a user on this organisation", + }, + allowExternalIDPVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if a user is allowed to add a defined identity provider. E.g. Google auth", + }, + forceMFAVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if a user MUST use a multi factor to log in", + }, + forceMFALocalOnlyVar: { + Type: schema.TypeBool, + Required: true, + Description: "if activated, ZITADEL only enforces MFA on local authentications. On authentications through MFA, ZITADEL won't prompt for MFA.", + }, + passwordlessTypeVar: { + Type: schema.TypeString, + Required: true, + Description: "defines if passwordless is allowed for users", + }, + hidePasswordResetVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if password reset link should be shown in the login screen", + }, + ignoreUnknownUsernamesVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if unknown username on login screen directly return an error or always display the password screen", + }, + DefaultRedirectURIVar: { + Type: schema.TypeString, + Required: true, + Description: "defines where the user will be redirected to if the login is started without app context (e.g. from mail)", + }, + passwordCheckLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + externalLoginCheckLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + mfaInitSkipLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + secondFactorCheckLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + multiFactorCheckLifetimeVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + secondFactorsVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "allowed second factors", + }, + multiFactorsVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "allowed multi factors", + }, + idpsVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "allowed idps to login or register", + }, + allowDomainDiscovery: { + Type: schema.TypeBool, + Optional: true, + Description: "if set to true, the suffix (@domain.com) of an unknown username input on the login screen will be matched against the org domains and will redirect to the registration of that organisation on success.", + }, + disableLoginWithEmail: { + Type: schema.TypeBool, + Optional: true, + Description: "defines if user can additionally (to the loginname) be identified by their verified email address", + }, + disableLoginWithPhone: { + Type: schema.TypeBool, + Optional: true, + Description: "defines if user can additionally (to the loginname) be identified by their verified phone number", + }, + }, + CreateContext: create, + UpdateContext: update, + DeleteContext: delete, + ReadContext: read, + Importer: helper.ImportWithOptionalOrg(), + } +} diff --git a/zitadel/login_policy/resource_test.go b/zitadel/login_policy/resource_test.go new file mode 100644 index 00000000..fe87ae27 --- /dev/null +++ b/zitadel/login_policy/resource_test.go @@ -0,0 +1,53 @@ +package login_policy_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_azure_ad/idp_azure_ad_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_google/idp_google_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/login_policy" +) + +func TestAccLoginPolicy(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_login_policy") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, login_policy.DefaultRedirectURIVar, exampleAttributes).AsString() + azureADDep, _ := idp_azure_ad_test_dep.Create(t, frame.BaseTestFrame, frame.Admin) + googleDep, _ := idp_google_test_dep.Create(t, frame.BaseTestFrame, frame.Admin) + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, azureADDep, googleDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "localhost:9090", + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + checkRemoteProperty(*frame)(""), + test_utils.ImportOrgId(frame), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetLoginPolicy(frame, &management.GetLoginPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetDefaultRedirectUri() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/login_texts/resource.go b/zitadel/login_texts/resource.go new file mode 100644 index 00000000..ba0ff030 --- /dev/null +++ b/zitadel/login_texts/resource.go @@ -0,0 +1,251 @@ +package login_texts + +import ( + "context" + "strings" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &loginTextsResource{} +) + +func New() resource.Resource { + return &loginTextsResource{} +} + +type loginTextsResource struct { + clientInfo *helper.ClientInfo +} + +func (r *loginTextsResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_login_texts" +} + +func (r *loginTextsResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return text.GenSchemaLoginCustomText(ctx) +} + +func (r *loginTextsResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *loginTextsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.LoginCustomText{} + resp.Diagnostics.Append(text.CopyLoginCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal login texts", err.Error()) + return + } + zReq := &management.SetCustomLoginTextsRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal login texts", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomLoginText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create login texts", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *loginTextsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + orgID, language := getID(ctx, state) + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomLoginTexts(ctx, &management.GetCustomLoginTextsRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyLoginCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *loginTextsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.LoginCustomText{} + resp.Diagnostics.Append(text.CopyLoginCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal login texts", err.Error()) + return + } + zReq := &management.SetCustomLoginTextsRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal login texts", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomLoginText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update login texts", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *loginTextsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + orgID, language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomLoginTextToDefault(ctx, &management.ResetCustomLoginTextsToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete login texts", err.Error()) + return + } +} + +func setID(obj types.Object, orgID string, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(orgID + "_" + language) + attrs[helper.OrgIDVar] = types.StringValue(orgID) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) (string, string) { + id := helper.GetStringFromAttr(ctx, obj.Attributes(), "id") + parts := strings.Split(id, "_") + if len(parts) == 2 { + return parts[0], parts[1] + } + return helper.GetStringFromAttr(ctx, obj.Attributes(), helper.OrgIDVar), helper.GetStringFromAttr(ctx, obj.Attributes(), LanguageVar) +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(plan.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(state.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} diff --git a/zitadel/login_texts/resource_test.go b/zitadel/login_texts/resource_test.go new file mode 100644 index 00000000..868cbeab --- /dev/null +++ b/zitadel/login_texts/resource_test.go @@ -0,0 +1,51 @@ +package login_texts_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/login_texts" +) + +func TestAccLoginTexts(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_login_texts") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := "example" + exampleLanguage := test_utils.AttributeValue(t, login_texts.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtext", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^\d{18}_%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)(""), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomLoginTexts(frame, &management.GetCustomLoginTextsRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetEmailVerificationDoneText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/machine_key/const.go b/zitadel/machine_key/const.go new file mode 100644 index 00000000..6d1d2bdf --- /dev/null +++ b/zitadel/machine_key/const.go @@ -0,0 +1,9 @@ +package machine_key + +const ( + keyIDVar = "key_id" + UserIDVar = "user_id" + keyTypeVar = "key_type" + KeyDetailsVar = "key_details" + ExpirationDateVar = "expiration_date" +) diff --git a/zitadel/machine_key/funcs.go b/zitadel/machine_key/funcs.go new file mode 100644 index 00000000..b018cd2f --- /dev/null +++ b/zitadel/machine_key/funcs.go @@ -0,0 +1,118 @@ +package machine_key + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/authn" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveMachineKey(ctx, &management.RemoveMachineKeyRequest{ + UserId: d.Get(UserIDVar).(string), + KeyId: d.Id(), + }) + if err != nil { + return diag.Errorf("failed to delete machine key: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + orgID := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, orgID) + if err != nil { + return diag.FromErr(err) + } + + keyType := d.Get(keyTypeVar).(string) + req := &management.AddMachineKeyRequest{ + UserId: d.Get(UserIDVar).(string), + Type: authn.KeyType(authn.KeyType_value[keyType]), + } + + if expiration, ok := d.GetOk(ExpirationDateVar); ok { + t, err := time.Parse(time.RFC3339, expiration.(string)) + if err != nil { + return diag.Errorf("failed to parse time: %v", err) + } + req.ExpirationDate = timestamppb.New(t) + } + + resp, err := client.AddMachineKey(ctx, req) + if err != nil { + return diag.FromErr(err) + } + d.SetId(resp.GetKeyId()) + if err := d.Set(KeyDetailsVar, string(resp.GetKeyDetails())); err != nil { + return diag.FromErr(err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + orgID := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, orgID) + if err != nil { + return diag.FromErr(err) + } + + userID := d.Get(UserIDVar).(string) + resp, err := client.GetMachineKeyByIDs(ctx, &management.GetMachineKeyByIDsRequest{ + UserId: userID, + KeyId: d.Id(), + }) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get machine key") + } + + d.SetId(resp.GetKey().GetId()) + set := map[string]interface{}{ + ExpirationDateVar: resp.GetKey().GetExpirationDate().AsTime().Format(time.RFC3339), + UserIDVar: userID, + helper.OrgIDVar: orgID, + keyTypeVar: resp.GetKey().GetType().String(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of machine key: %v", k, err) + } + } + return nil +} diff --git a/zitadel/machine_key/resource.go b/zitadel/machine_key/resource.go new file mode 100644 index 00000000..472310b6 --- /dev/null +++ b/zitadel/machine_key/resource.go @@ -0,0 +1,55 @@ +package machine_key + +import ( + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/authn" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a machine key", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + UserIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the user", + ForceNew: true, + }, + keyTypeVar: { + Type: schema.TypeString, + Required: true, + Description: "Type of the machine key" + helper.DescriptionEnumValuesList(authn.KeyType_name), + ForceNew: true, + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(keyTypeVar, value, authn.KeyType_value) + }, + }, + ExpirationDateVar: { + Type: schema.TypeString, + Optional: true, + Description: "Expiration date of the machine key in the RFC3339 format", + ForceNew: true, + Computed: true, + }, + KeyDetailsVar: { + Type: schema.TypeString, + Computed: true, + Description: "Value of the machine key", + Sensitive: true, + }, + }, + DeleteContext: delete, + CreateContext: create, + ReadContext: read, + Importer: helper.ImportWithIDAndOptionalOrg( + keyIDVar, + helper.NewImportAttribute(UserIDVar, helper.ConvertID, false), + helper.NewImportAttribute(KeyDetailsVar, helper.ConvertJSON, true), + ), + } +} diff --git a/zitadel/machine_key/resource_test.go b/zitadel/machine_key/resource_test.go new file mode 100644 index 00000000..ad2ebc04 --- /dev/null +++ b/zitadel/machine_key/resource_test.go @@ -0,0 +1,59 @@ +package machine_key_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/machine_key" + "github.com/zitadel/terraform-provider-zitadel/zitadel/machine_user/machine_user_test_dep" +) + +func TestAccMachineKey(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_machine_key") + userDep, userID := machine_user_test_dep.Create(t, frame) + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, machine_key.ExpirationDateVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, userDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "2051-01-01T00:00:00Z", + "", "", "", + false, + checkRemoteProperty(*frame, userID), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(*frame, userID), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, machine_key.UserIDVar), + test_utils.ImportOrgId(frame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, machine_key.KeyDetailsVar), + ), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame, userID string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetMachineKeyByIDs(frame, &management.GetMachineKeyByIDsRequest{ + UserId: userID, + KeyId: frame.State(state).ID, + }) + if err != nil { + return err + } + actual := resp.GetKey().GetExpirationDate().AsTime().Format("2006-01-02T15:04:05Z") + if expect != actual { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/machine_user/const.go b/zitadel/machine_user/const.go new file mode 100644 index 00000000..8233f9bc --- /dev/null +++ b/zitadel/machine_user/const.go @@ -0,0 +1,20 @@ +package machine_user + +import ( + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/user" +) + +const ( + UserIDVar = "user_id" + userStateVar = "state" + UserNameVar = "user_name" + loginNamesVar = "login_names" + preferredLoginNameVar = "preferred_login_name" + nameVar = "name" + DescriptionVar = "description" + accessTokenTypeVar = "access_token_type" +) + +var ( + defaultAccessTokenType = user.AccessTokenType_name[0] +) diff --git a/zitadel/machine_user/datasource.go b/zitadel/machine_user/datasource.go new file mode 100644 index 00000000..3b07ae86 --- /dev/null +++ b/zitadel/machine_user/datasource.go @@ -0,0 +1,60 @@ +package machine_user + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a serviceaccount situated under an organization, which then can be authorized through memberships or direct grants on other resources.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDDatasourceField, + UserIDVar: { + Type: schema.TypeString, + Required: true, + Description: "The ID of this resource.", + }, + userStateVar: { + Type: schema.TypeString, + Computed: true, + Description: "State of the user", + }, + UserNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Username", + }, + loginNamesVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "Loginnames", + }, + preferredLoginNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Preferred login name", + }, + + nameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Name of the machine user", + }, + DescriptionVar: { + Type: schema.TypeString, + Computed: true, + Description: "Description of the user", + }, + accessTokenTypeVar: { + Type: schema.TypeString, + Computed: true, + Description: "Access token type", + }}, + ReadContext: read, + } +} diff --git a/zitadel/machine_user/funcs.go b/zitadel/machine_user/funcs.go new file mode 100644 index 00000000..dbad970d --- /dev/null +++ b/zitadel/machine_user/funcs.go @@ -0,0 +1,144 @@ +package machine_user + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/user" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveUser(ctx, &management.RemoveUserRequest{ + Id: d.Id(), + }) + if err != nil { + return diag.Errorf("failed to delete user: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + respUser, err := client.AddMachineUser(ctx, &management.AddMachineUserRequest{ + UserName: d.Get(UserNameVar).(string), + Name: d.Get(nameVar).(string), + Description: d.Get(DescriptionVar).(string), + AccessTokenType: user.AccessTokenType(user.AccessTokenType_value[(d.Get(accessTokenTypeVar).(string))]), + }) + if err != nil { + return diag.Errorf("failed to create machine user: %v", err) + } + d.SetId(respUser.UserId) + // To avoid diffs for terraform plan -refresh=false right after creation, we query and set the computed values. + // The acceptance tests rely on this, too. + return read(ctx, d, m) +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChange(UserNameVar) { + _, err = client.UpdateUserName(ctx, &management.UpdateUserNameRequest{ + UserId: d.Id(), + UserName: d.Get(UserNameVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update username: %v", err) + } + } + + if d.HasChanges(nameVar, DescriptionVar, accessTokenTypeVar) { + _, err := client.UpdateMachine(ctx, &management.UpdateMachineRequest{ + UserId: d.Id(), + Name: d.Get(nameVar).(string), + Description: d.Get(DescriptionVar).(string), + AccessTokenType: user.AccessTokenType(user.AccessTokenType_value[(d.Get(accessTokenTypeVar).(string))]), + }) + if err != nil { + return diag.Errorf("failed to update machine user: %v", err) + } + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + respUser, err := client.GetUserByID(ctx, &management.GetUserByIDRequest{Id: helper.GetID(d, UserIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get user") + } + + user := respUser.GetUser() + set := map[string]interface{}{ + helper.OrgIDVar: user.GetDetails().GetResourceOwner(), + userStateVar: user.GetState().String(), + UserNameVar: user.GetUserName(), + loginNamesVar: user.GetLoginNames(), + preferredLoginNameVar: user.GetPreferredLoginName(), + } + if machine := user.GetMachine(); machine != nil { + set[nameVar] = machine.GetName() + set[DescriptionVar] = machine.GetDescription() + set[accessTokenTypeVar] = machine.GetAccessTokenType().String() + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of user: %v", k, err) + } + } + d.SetId(user.GetId()) + return nil +} diff --git a/zitadel/machine_user/machine_user_test_dep/dependency.go b/zitadel/machine_user/machine_user_test_dep/dependency.go new file mode 100644 index 00000000..a32a2527 --- /dev/null +++ b/zitadel/machine_user/machine_user_test_dep/dependency.go @@ -0,0 +1,21 @@ +package machine_user_test_dep + +import ( + "testing" + + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/machine_user" +) + +func Create(t *testing.T, frame *test_utils.OrgTestFrame) (string, string) { + return test_utils.CreateDefaultDependency(t, "zitadel_machine_user", machine_user.UserIDVar, func() (string, error) { + user, err := frame.AddMachineUser(frame, &management.AddMachineUserRequest{ + UserName: frame.UniqueResourcesID, + Name: "Don't care", + }) + userID := user.GetUserId() + return userID, err + }) +} diff --git a/zitadel/machine_user/resource.go b/zitadel/machine_user/resource.go new file mode 100644 index 00000000..208d97de --- /dev/null +++ b/zitadel/machine_user/resource.go @@ -0,0 +1,70 @@ +package machine_user + +import ( + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/user" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a serviceaccount situated under an organization, which then can be authorized through memberships or direct grants on other resources.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + userStateVar: { + Type: schema.TypeString, + Computed: true, + Description: "State of the user", + /* Not necessary as long as only active users are created + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return EnumValueValidation(userStateVar, value.(string), user.UserState_value) + },*/ + }, + UserNameVar: { + Type: schema.TypeString, + Required: true, + Description: "Username", + }, + loginNamesVar: { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "Loginnames", + }, + preferredLoginNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Preferred login name", + }, + nameVar: { + Type: schema.TypeString, + Required: true, + Description: "Name of the machine user", + }, + DescriptionVar: { + Type: schema.TypeString, + Optional: true, + Description: "Description of the user", + }, + accessTokenTypeVar: { + Type: schema.TypeString, + Optional: true, + Description: "Access token type" + helper.DescriptionEnumValuesList(user.AccessTokenType_name), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(accessTokenTypeVar, value, user.AccessTokenType_value) + }, + Default: defaultAccessTokenType, + }, + }, + ReadContext: read, + CreateContext: create, + DeleteContext: delete, + UpdateContext: update, + Importer: helper.ImportWithIDAndOptionalOrg(UserIDVar), + } +} diff --git a/zitadel/machine_user/resource_test.go b/zitadel/machine_user/resource_test.go new file mode 100644 index 00000000..42d484fd --- /dev/null +++ b/zitadel/machine_user/resource_test.go @@ -0,0 +1,55 @@ +package machine_user_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/machine_user" +) + +func TestAccMachineUser(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_machine_user") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleUsername := test_utils.AttributeValue(t, machine_user.UserNameVar, exampleAttributes).AsString() + resourceExample = strings.Replace(resourceExample, exampleUsername, frame.UniqueResourcesID, 1) + exampleProperty := test_utils.AttributeValue(t, machine_user.DescriptionVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedproperty", + "", "", "", + false, + checkRemoteProperty(frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(frame), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetUserByID(frame, &management.GetUserByIDRequest{Id: frame.State(state).ID}) + if err != nil { + return err + } + actual := remoteResource.GetUser().GetMachine().GetDescription() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/notification_policy/const.go b/zitadel/notification_policy/const.go new file mode 100644 index 00000000..2576d012 --- /dev/null +++ b/zitadel/notification_policy/const.go @@ -0,0 +1,5 @@ +package notification_policy + +const ( + passwordChangeVar = "password_change" +) diff --git a/zitadel/notification_policy/funcs.go b/zitadel/notification_policy/funcs.go new file mode 100644 index 00000000..7d8092f4 --- /dev/null +++ b/zitadel/notification_policy/funcs.go @@ -0,0 +1,111 @@ +package notification_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + _, err = client.ResetNotificationPolicyToDefault(ctx, &management.ResetNotificationPolicyToDefaultRequest{}) + if err != nil { + return diag.Errorf("failed to reset notification policy: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + org := helper.GetID(d, helper.OrgIDVar) + if d.HasChanges(passwordChangeVar) { + _, err = client.UpdateCustomNotificationPolicy(ctx, &management.UpdateCustomNotificationPolicyRequest{ + PasswordChange: d.Get(passwordChangeVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to update notification policy: %v", err) + } + } + d.SetId(org) + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + org := d.Get(helper.OrgIDVar).(string) + _, err = client.AddCustomNotificationPolicy(ctx, &management.AddCustomNotificationPolicyRequest{ + PasswordChange: d.Get(passwordChangeVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to create notification policy: %v", err) + } + d.SetId(org) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetNotificationPolicy(ctx, &management.GetNotificationPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get notification policy") + } + policy := resp.Policy + if policy.GetIsDefault() == true { + d.SetId("") + return nil + } + set := map[string]interface{}{ + helper.OrgIDVar: policy.GetDetails().GetResourceOwner(), + passwordChangeVar: policy.GetPasswordChange(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of notification: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/notification_policy/resource.go b/zitadel/notification_policy/resource.go new file mode 100644 index 00000000..339a8e4c --- /dev/null +++ b/zitadel/notification_policy/resource.go @@ -0,0 +1,26 @@ +package notification_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the custom notification policy of an organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + passwordChangeVar: { + Type: schema.TypeBool, + Required: true, + Description: "Send notification if a user changes his password", + }, + }, + ReadContext: read, + CreateContext: create, + DeleteContext: delete, + UpdateContext: update, + Importer: helper.ImportWithOptionalOrg(), + } +} diff --git a/zitadel/notification_policy/resource_test.go b/zitadel/notification_policy/resource_test.go new file mode 100644 index 00000000..40c16736 --- /dev/null +++ b/zitadel/notification_policy/resource_test.go @@ -0,0 +1,50 @@ +package notification_policy_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccNotificationPolicy(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_notification_policy") + resourceExample, _ := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := false + initialProperty := true + updatedProperty := false + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + initialProperty, updatedProperty, + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + checkRemoteProperty(*frame)(true), + test_utils.ImportOrgId(frame), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame) func(bool) resource.TestCheckFunc { + return func(expect bool) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetNotificationPolicy(frame, &management.GetNotificationPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetPasswordChange() + if actual != expect { + return fmt.Errorf("expected %t, but got %t", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/org/const.go b/zitadel/org/const.go new file mode 100644 index 00000000..450075aa --- /dev/null +++ b/zitadel/org/const.go @@ -0,0 +1,12 @@ +package org + +const ( + OrgIDVar = "id" + orgIDsVar = "ids" + NameVar = "name" + nameMethodVar = "name_method" + DomainVar = "domain" + domainMethodVar = "domain_method" + stateVar = "state" + primaryDomainVar = "primary_domain" +) diff --git a/zitadel/org/datasource.go b/zitadel/org/datasource.go new file mode 100644 index 00000000..80fcf297 --- /dev/null +++ b/zitadel/org/datasource.go @@ -0,0 +1,100 @@ +package org + +import ( + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/object" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/org" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing an organization in ZITADEL, which is the highest level after the instance and contains several other resource including policies if the configuration differs to the default policies on the instance.", + Schema: map[string]*schema.Schema{ + OrgIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the organization", + ValidateDiagFunc: func(i interface{}, path cty.Path) diag.Diagnostics { + _, err := helper.ConvertID(i.(string)) + return diag.FromErr(err) + }, + }, + NameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Name of the org.", + }, + stateVar: { + Type: schema.TypeString, + Computed: true, + Description: "State of the org" + helper.DescriptionEnumValuesList(org.OrgState_name), + }, + primaryDomainVar: { + Type: schema.TypeString, + Computed: true, + Description: "Primary domain of the org", + }, + }, + ReadContext: get, + } +} + +func ListDatasources() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing an organization in ZITADEL, which is the highest level after the instance and contains several other resource including policies if the configuration differs to the default policies on the instance.", + Schema: map[string]*schema.Schema{ + orgIDsVar: { + Type: schema.TypeList, + Computed: true, + Description: "A set of all organization IDs.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + NameVar: { + Type: schema.TypeString, + Optional: true, + Description: "Name of the org.", + }, + nameMethodVar: { + Type: schema.TypeString, + Optional: true, + Description: "Method for querying orgs by name" + helper.DescriptionEnumValuesList(object.TextQueryMethod_name), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(nameMethodVar, value, object.TextQueryMethod_value) + }, + Default: object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE.String(), + }, + DomainVar: { + Type: schema.TypeString, + Optional: true, + Description: "A domain of the org.", + }, + domainMethodVar: { + Type: schema.TypeString, + Optional: true, + Description: "Method for querying orgs by domain" + helper.DescriptionEnumValuesList(object.TextQueryMethod_name), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(domainMethodVar, value, object.TextQueryMethod_value) + }, + InputDefault: object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE.String(), + }, + stateVar: { + Type: schema.TypeString, + Optional: true, + Description: "State of the org" + helper.DescriptionEnumValuesList(org.OrgState_name), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(stateVar, value, org.OrgState_value) + }, + }, + primaryDomainVar: { + Type: schema.TypeString, + Computed: true, + Description: "Primary domain of the org", + }, + }, + ReadContext: list, + } +} diff --git a/zitadel/org/datasource_test.go b/zitadel/org/datasource_test.go new file mode 100644 index 00000000..d40092af --- /dev/null +++ b/zitadel/org/datasource_test.go @@ -0,0 +1,75 @@ +package org_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org" +) + +func TestAccOrgDatasource_ID(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_org") + orgName := "org_datasource_" + frame.UniqueResourcesID + otherFrame := frame.AnotherOrg(t, orgName) + test_utils.RunDatasourceTest( + t, + otherFrame.BaseTestFrame, + otherFrame.AsOrgDefaultDependency, + nil, + map[string]string{ + "id": otherFrame.OrgID, + "name": orgName, + "state": "ORG_STATE_ACTIVE", + }, + ) +} + +func TestAccOrgsDatasources_ID_Name_Match(t *testing.T) { + datasourceName := "zitadel_orgs" + frame := test_utils.NewOrgTestFrame(t, datasourceName) + config, attributes := test_utils.ReadExample(t, test_utils.Datasources, datasourceName) + exampleName := test_utils.AttributeValue(t, org.NameVar, attributes).AsString() + exampleDomain := test_utils.AttributeValue(t, org.DomainVar, attributes).AsString() + orgName := fmt.Sprintf("%s-%s", exampleName, frame.UniqueResourcesID) + // for-each is not supported in acceptance tests, so we cut the example down to the first block + // https://github.com/hashicorp/terraform-plugin-sdk/issues/536 + config = strings.Join(strings.Split(config, "\n")[0:7], "\n") + config = strings.Replace(config, exampleName, orgName, 1) + config = strings.Replace(config, exampleDomain, orgName, 1) + otherFrame := frame.AnotherOrg(t, orgName) + test_utils.RunDatasourceTest( + t, + otherFrame.BaseTestFrame, + config, + checkRemoteProperty(otherFrame, idFromFrame(otherFrame))(orgName), + map[string]string{ + "ids.0": otherFrame.OrgID, + "ids.#": "1", + }, + ) +} + +func TestAccOrgsDatasources_ID_Name_Mismatch(t *testing.T) { + datasourceName := "zitadel_orgs" + frame := test_utils.NewOrgTestFrame(t, datasourceName) + config, attributes := test_utils.ReadExample(t, test_utils.Datasources, datasourceName) + orgName := fmt.Sprintf("%s_%s", test_utils.AttributeValue(t, org.NameVar, attributes), frame.UniqueResourcesID) + otherFrame := frame.AnotherOrg(t, orgName) + test_utils.RunDatasourceTest( + t, + otherFrame.BaseTestFrame, + config, + checkRemoteProperty(otherFrame, idFromFrame(otherFrame))(orgName), + map[string]string{"ids.#": "0"}, + ) +} + +func idFromFrame(frame *test_utils.OrgTestFrame) func(state *terraform.State) string { + return func(state *terraform.State) string { + return frame.OrgID + } +} diff --git a/zitadel/org/funcs.go b/zitadel/org/funcs.go new file mode 100644 index 00000000..c7879c28 --- /dev/null +++ b/zitadel/org/funcs.go @@ -0,0 +1,165 @@ +package org + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/object" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/org" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + _, err = client.RemoveOrg(ctx, &admin.RemoveOrgRequest{ + OrgId: d.Id(), + }) + if err != nil { + return diag.FromErr(err) + } + d.SetId("") + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, "") + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddOrg(ctx, &management.AddOrgRequest{ + Name: d.Get(NameVar).(string), + }) + if err != nil { + return diag.FromErr(err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, d.Id()) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateOrg(ctx, &management.UpdateOrgRequest{ + Name: d.Get(NameVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update org: %v", err) + } + return nil +} + +func get(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started get") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + orgID := helper.GetID(d, OrgIDVar) + resp, err := client.GetOrgByID(ctx, &admin.GetOrgByIDRequest{ + Id: orgID, + }) + if err != nil { + return diag.Errorf("error while getting org by id %s: %v", orgID, err) + } + remoteOrg := resp.GetOrg() + d.SetId(remoteOrg.Id) + if err := d.Set(NameVar, remoteOrg.Name); err != nil { + return diag.Errorf("error while setting org name %s: %v", remoteOrg.Name, err) + } + if err := d.Set(primaryDomainVar, remoteOrg.PrimaryDomain); err != nil { + return diag.Errorf("error while setting org primary domain %s: %v", remoteOrg.PrimaryDomain, err) + } + state := org.OrgState_name[int32(remoteOrg.State)] + if err := d.Set(stateVar, state); err != nil { + return diag.Errorf("error while setting org state %s: %v", state, err) + } + return nil +} + +func list(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started list") + orgName := d.Get(NameVar).(string) + orgNameMethod := d.Get(nameMethodVar).(string) + orgDomain := d.Get(DomainVar).(string) + orgDomainMethod := d.Get(domainMethodVar).(string) + orgState := d.Get(stateVar).(string) + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + req := &admin.ListOrgsRequest{} + if orgName != "" { + req.Queries = append(req.Queries, &org.OrgQuery{ + Query: &org.OrgQuery_NameQuery{ + NameQuery: &org.OrgNameQuery{ + Name: orgName, + Method: object.TextQueryMethod(object.TextQueryMethod_value[orgNameMethod]), + }, + }, + }) + } + if orgState != "" { + req.Queries = append(req.Queries, &org.OrgQuery{ + Query: &org.OrgQuery_StateQuery{ + StateQuery: &org.OrgStateQuery{ + State: org.OrgState(org.OrgState_value[orgState]), + }, + }, + }) + } + if orgDomain != "" { + req.Queries = append(req.Queries, &org.OrgQuery{ + Query: &org.OrgQuery_DomainQuery{ + DomainQuery: &org.OrgDomainQuery{ + Domain: orgDomain, + Method: object.TextQueryMethod(object.TextQueryMethod_value[orgDomainMethod]), + }, + }, + }) + } + resp, err := client.ListOrgs(ctx, req) + if err != nil { + return diag.Errorf("error while getting org by id %s: %v", orgName, err) + } + orgIDs := make([]string, len(resp.Result)) + for i, org := range resp.Result { + orgIDs[i] = org.Id + } + // If the ID is blank, the datasource is deleted and not usable. + d.SetId("-") + return diag.FromErr(d.Set(orgIDsVar, orgIDs)) +} diff --git a/zitadel/org/org_test_dep/dependency.go b/zitadel/org/org_test_dep/dependency.go new file mode 100644 index 00000000..4f8b8944 --- /dev/null +++ b/zitadel/org/org_test_dep/dependency.go @@ -0,0 +1,18 @@ +package org_test_dep + +import ( + "fmt" + "strings" + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org" +) + +func Create(t *testing.T, frame *test_utils.OrgTestFrame, resourceName string) (string, string, *test_utils.OrgTestFrame) { + otherFrame := frame.AnotherOrg(t, fmt.Sprintf("%s_%s", resourceName, frame.UniqueResourcesID)) + cfg, id := test_utils.CreateDefaultDependency(t, "zitadel_org", org.OrgIDVar, func() (string, error) { + return otherFrame.OrgID, nil + }) + return strings.Replace(cfg, "default", resourceName, 1), id, otherFrame +} diff --git a/zitadel/org/remote_test.go b/zitadel/org/remote_test.go new file mode 100644 index 00000000..5a8ee48d --- /dev/null +++ b/zitadel/org/remote_test.go @@ -0,0 +1,31 @@ +package org_test + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/org" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, id func(state *terraform.State) string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.Admin.GetOrgByID(frame, &admin.GetOrgByIDRequest{Id: id(state)}) + if err != nil { + return err + } + actual := remoteResource.GetOrg().GetName() + if remoteResource.GetOrg().GetState() == org.OrgState_ORG_STATE_REMOVED { + return fmt.Errorf("org is removed: %w", test_utils.ErrNotFound) + } + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/org/resource.go b/zitadel/org/resource.go new file mode 100644 index 00000000..d43da0cf --- /dev/null +++ b/zitadel/org/resource.go @@ -0,0 +1,35 @@ +package org + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing an organization in ZITADEL, which is the highest level after the instance and contains several other resource including policies if the configuration differs to the default policies on the instance.", + Schema: map[string]*schema.Schema{ + NameVar: { + Type: schema.TypeString, + Required: true, + Description: "Name of the org", + }, + primaryDomainVar: { + Type: schema.TypeString, + Computed: true, + Description: "Primary domain of the org", + }, + stateVar: { + Type: schema.TypeString, + Computed: true, + Description: "State of the org", + }, + }, + CreateContext: create, + DeleteContext: delete, + ReadContext: get, + UpdateContext: update, + Importer: helper.ImportWithID(OrgIDVar), + } +} diff --git a/zitadel/org/resource_test.go b/zitadel/org/resource_test.go new file mode 100644 index 00000000..8ecbae2a --- /dev/null +++ b/zitadel/org/resource_test.go @@ -0,0 +1,38 @@ +package org_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org" +) + +func TestAccOrg(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_org") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, org.NameVar, exampleAttributes).AsString() + initialProperty := "initialorgname_" + frame.UniqueResourcesID + updatedProperty := "updatedorgname_" + frame.UniqueResourcesID + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + initialProperty, updatedProperty, + "", "", "", + false, + checkRemoteProperty(frame, idFromState(frame)), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(frame, idFromState(frame)), updatedProperty), + test_utils.ImportResourceId(frame.BaseTestFrame), + ) +} + +func idFromState(frame *test_utils.OrgTestFrame) func(*terraform.State) string { + return func(state *terraform.State) string { + return frame.State(state).ID + } +} diff --git a/zitadel/org_idp_azure_ad/datasource.go b/zitadel/org_idp_azure_ad/datasource.go new file mode 100644 index 00000000..e059b15c --- /dev/null +++ b/zitadel/org_idp_azure_ad/datasource.go @@ -0,0 +1,31 @@ +package org_idp_azure_ad + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_azure_ad" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing an Azure AD IdP of the organization.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + helper.OrgIDVar: helper.OrgIDDatasourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + idp_azure_ad.TenantTypeVar: idp_azure_ad.TenantTypeDataSourceField, + idp_azure_ad.TenantIDVar: idp_azure_ad.TenantIDDataSourceField, + idp_azure_ad.EmailVerifiedVar: idp_azure_ad.EmailVerifiedDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/org_idp_azure_ad/funcs.go b/zitadel/org_idp_azure_ad/funcs.go new file mode 100644 index 00000000..1f785355 --- /dev/null +++ b/zitadel/org_idp_azure_ad/funcs.go @@ -0,0 +1,116 @@ +package org_idp_azure_ad + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/idp" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_azure_ad" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + tenant, err := idp_azure_ad.ConstructTenant(d) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddAzureADProvider(ctx, &management.AddAzureADProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + Tenant: tenant, + EmailVerified: idp_utils.BoolValue(d, idp_azure_ad.EmailVerifiedVar), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + tenant, err := idp_azure_ad.ConstructTenant(d) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateAzureADProvider(ctx, &management.UpdateAzureADProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + Tenant: tenant, + EmailVerified: idp_utils.BoolValue(d, idp_azure_ad.EmailVerifiedVar), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &management.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + respIdp := resp.GetIdp() + cfg := respIdp.GetConfig() + specificCfg := cfg.GetAzureAd() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + helper.OrgIDVar: respIdp.GetDetails().GetResourceOwner(), + idp_utils.NameVar: respIdp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + idp_azure_ad.EmailVerifiedVar: specificCfg.GetEmailVerified(), + idp_azure_ad.TenantTypeVar: idp.AzureADTenantType_name[int32(specificCfg.GetTenant().GetTenantType())], + idp_azure_ad.TenantIDVar: specificCfg.GetTenant().GetTenantId(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(respIdp.Id) + return nil +} diff --git a/zitadel/org_idp_azure_ad/resource.go b/zitadel/org_idp_azure_ad/resource.go new file mode 100644 index 00000000..ed6285ef --- /dev/null +++ b/zitadel/org_idp_azure_ad/resource.go @@ -0,0 +1,35 @@ +package org_idp_azure_ad + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_azure_ad" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing an Azure AD IdP on the organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + idp_azure_ad.TenantTypeVar: idp_azure_ad.TenantTypeResourceField, + idp_azure_ad.TenantIDVar: idp_azure_ad.TenantIDResourceField, + idp_azure_ad.EmailVerifiedVar: idp_azure_ad.EmailVerifiedResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: org_idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalOrgAndSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/org_idp_azure_ad/resource_test.go b/zitadel/org_idp_azure_ad/resource_test.go new file mode 100644 index 00000000..77ab1615 --- /dev/null +++ b/zitadel/org_idp_azure_ad/resource_test.go @@ -0,0 +1,12 @@ +package org_idp_azure_ad_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils/org_idp_test_utils" +) + +func TestAccOrgIdPAzureAD(t *testing.T) { + org_idp_test_utils.RunOrgLifecyleTest(t, "zitadel_org_idp_azure_ad", idp_utils.ClientSecretVar) +} diff --git a/zitadel/org_idp_github/datasource.go b/zitadel/org_idp_github/datasource.go new file mode 100644 index 00000000..72985200 --- /dev/null +++ b/zitadel/org_idp_github/datasource.go @@ -0,0 +1,27 @@ +package org_idp_github + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a GitHub IdP of the organization.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + helper.OrgIDVar: helper.OrgIDDatasourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/org_idp_github/funcs.go b/zitadel/org_idp_github/funcs.go new file mode 100644 index 00000000..83d89348 --- /dev/null +++ b/zitadel/org_idp_github/funcs.go @@ -0,0 +1,99 @@ +package org_idp_github + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddGitHubProvider(ctx, &management.AddGitHubProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateGitHubProvider(ctx, &management.UpdateGitHubProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &management.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetGithub() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + helper.OrgIDVar: idp.GetDetails().GetResourceOwner(), + idp_utils.NameVar: idp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/org_idp_github/resource.go b/zitadel/org_idp_github/resource.go new file mode 100644 index 00000000..04dd5f9e --- /dev/null +++ b/zitadel/org_idp_github/resource.go @@ -0,0 +1,31 @@ +package org_idp_github + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a GitHub IdP on the organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: org_idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalOrgAndSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/org_idp_github/resource_test.go b/zitadel/org_idp_github/resource_test.go new file mode 100644 index 00000000..fae18984 --- /dev/null +++ b/zitadel/org_idp_github/resource_test.go @@ -0,0 +1,12 @@ +package org_idp_github_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils/org_idp_test_utils" +) + +func TestAccOrgIdPGitHub(t *testing.T) { + org_idp_test_utils.RunOrgLifecyleTest(t, "zitadel_org_idp_github", idp_utils.ClientSecretVar) +} diff --git a/zitadel/org_idp_github_es/datasource.go b/zitadel/org_idp_github_es/datasource.go new file mode 100644 index 00000000..12cd5eda --- /dev/null +++ b/zitadel/org_idp_github_es/datasource.go @@ -0,0 +1,31 @@ +package org_idp_github_es + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_github_es" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a GitHub Enterprise IdP of the organization.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + helper.OrgIDVar: helper.OrgIDDatasourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + idp_github_es.AuthorizationEndpointVar: idp_github_es.AuthorizationEndpointDataSourceField, + idp_github_es.TokenEndpointVar: idp_github_es.TokenEndpointDataSourceField, + idp_github_es.UserEndpointVar: idp_github_es.UserEndpointDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/org_idp_github_es/funcs.go b/zitadel/org_idp_github_es/funcs.go new file mode 100644 index 00000000..972483b1 --- /dev/null +++ b/zitadel/org_idp_github_es/funcs.go @@ -0,0 +1,109 @@ +package org_idp_github_es + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_github_es" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddGitHubEnterpriseServerProvider(ctx, &management.AddGitHubEnterpriseServerProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + AuthorizationEndpoint: idp_utils.StringValue(d, idp_github_es.AuthorizationEndpointVar), + TokenEndpoint: idp_utils.StringValue(d, idp_github_es.TokenEndpointVar), + UserEndpoint: idp_utils.StringValue(d, idp_github_es.UserEndpointVar), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateGitHubEnterpriseServerProvider(ctx, &management.UpdateGitHubEnterpriseServerProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + AuthorizationEndpoint: idp_utils.StringValue(d, idp_github_es.AuthorizationEndpointVar), + TokenEndpoint: idp_utils.StringValue(d, idp_github_es.TokenEndpointVar), + UserEndpoint: idp_utils.StringValue(d, idp_github_es.UserEndpointVar), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &management.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetGithubEs() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + helper.OrgIDVar: idp.GetDetails().GetResourceOwner(), + idp_utils.NameVar: idp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + idp_github_es.AuthorizationEndpointVar: specificCfg.GetAuthorizationEndpoint(), + idp_github_es.TokenEndpointVar: specificCfg.GetTokenEndpoint(), + idp_github_es.UserEndpointVar: specificCfg.GetUserEndpoint(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/org_idp_github_es/resource.go b/zitadel/org_idp_github_es/resource.go new file mode 100644 index 00000000..5f06cd0a --- /dev/null +++ b/zitadel/org_idp_github_es/resource.go @@ -0,0 +1,35 @@ +package org_idp_github_es + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_github_es" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a GitHub Enterprise IdP on the organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + idp_github_es.AuthorizationEndpointVar: idp_github_es.AuthorizationEndpointResourceField, + idp_github_es.TokenEndpointVar: idp_github_es.TokenEndpointResourceField, + idp_github_es.UserEndpointVar: idp_github_es.UserEndpointResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: org_idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalOrgAndSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/org_idp_github_es/resource_test.go b/zitadel/org_idp_github_es/resource_test.go new file mode 100644 index 00000000..a14bf1e2 --- /dev/null +++ b/zitadel/org_idp_github_es/resource_test.go @@ -0,0 +1,12 @@ +package org_idp_github_es_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils/org_idp_test_utils" +) + +func TestAccOrgIdPGitHubES(t *testing.T) { + org_idp_test_utils.RunOrgLifecyleTest(t, "zitadel_org_idp_github_es", idp_utils.ClientSecretVar) +} diff --git a/zitadel/org_idp_gitlab/datasource.go b/zitadel/org_idp_gitlab/datasource.go new file mode 100644 index 00000000..fbfc468d --- /dev/null +++ b/zitadel/org_idp_gitlab/datasource.go @@ -0,0 +1,27 @@ +package org_idp_gitlab + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a GitLab IdP of the organization.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + helper.OrgIDVar: helper.OrgIDDatasourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/org_idp_gitlab/funcs.go b/zitadel/org_idp_gitlab/funcs.go new file mode 100644 index 00000000..818dc84f --- /dev/null +++ b/zitadel/org_idp_gitlab/funcs.go @@ -0,0 +1,99 @@ +package org_idp_gitlab + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddGitLabProvider(ctx, &management.AddGitLabProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateGitLabProvider(ctx, &management.UpdateGitLabProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &management.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetGitlab() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + helper.OrgIDVar: idp.GetDetails().GetResourceOwner(), + idp_utils.NameVar: idp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/org_idp_gitlab/resource.go b/zitadel/org_idp_gitlab/resource.go new file mode 100644 index 00000000..a63c98b3 --- /dev/null +++ b/zitadel/org_idp_gitlab/resource.go @@ -0,0 +1,31 @@ +package org_idp_gitlab + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a GitLab IdP on the organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: org_idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalOrgAndSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/org_idp_gitlab/resource_test.go b/zitadel/org_idp_gitlab/resource_test.go new file mode 100644 index 00000000..2a289ddb --- /dev/null +++ b/zitadel/org_idp_gitlab/resource_test.go @@ -0,0 +1,12 @@ +package org_idp_gitlab_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils/org_idp_test_utils" +) + +func TestAccOrgIdPGitLab(t *testing.T) { + org_idp_test_utils.RunOrgLifecyleTest(t, "zitadel_org_idp_gitlab", idp_utils.ClientSecretVar) +} diff --git a/zitadel/org_idp_gitlab_self_hosted/datasource.go b/zitadel/org_idp_gitlab_self_hosted/datasource.go new file mode 100644 index 00000000..6d90e7d2 --- /dev/null +++ b/zitadel/org_idp_gitlab_self_hosted/datasource.go @@ -0,0 +1,29 @@ +package org_idp_gitlab_self_hosted + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_gitlab_self_hosted" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a GitLab Self Hosted IdP of the organization.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + helper.OrgIDVar: helper.OrgIDDatasourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + idp_gitlab_self_hosted.IssuerVar: idp_gitlab_self_hosted.IssuerDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/org_idp_gitlab_self_hosted/funcs.go b/zitadel/org_idp_gitlab_self_hosted/funcs.go new file mode 100644 index 00000000..efbc4d98 --- /dev/null +++ b/zitadel/org_idp_gitlab_self_hosted/funcs.go @@ -0,0 +1,103 @@ +package org_idp_gitlab_self_hosted + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_gitlab_self_hosted" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddGitLabSelfHostedProvider(ctx, &management.AddGitLabSelfHostedProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + Issuer: idp_utils.StringValue(d, idp_gitlab_self_hosted.IssuerVar), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateGitLabSelfHostedProvider(ctx, &management.UpdateGitLabSelfHostedProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + Issuer: idp_utils.StringValue(d, idp_gitlab_self_hosted.IssuerVar), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &management.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetGitlabSelfHosted() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + helper.OrgIDVar: idp.GetDetails().GetResourceOwner(), + idp_utils.NameVar: idp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + idp_gitlab_self_hosted.IssuerVar: specificCfg.GetIssuer(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/org_idp_gitlab_self_hosted/resource.go b/zitadel/org_idp_gitlab_self_hosted/resource.go new file mode 100644 index 00000000..cd184c46 --- /dev/null +++ b/zitadel/org_idp_gitlab_self_hosted/resource.go @@ -0,0 +1,33 @@ +package org_idp_gitlab_self_hosted + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_gitlab_self_hosted" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a GitLab Self Hosted IdP on the organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + idp_gitlab_self_hosted.IssuerVar: idp_gitlab_self_hosted.IssuerResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: org_idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalOrgAndSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/org_idp_gitlab_self_hosted/resource_test.go b/zitadel/org_idp_gitlab_self_hosted/resource_test.go new file mode 100644 index 00000000..429b1643 --- /dev/null +++ b/zitadel/org_idp_gitlab_self_hosted/resource_test.go @@ -0,0 +1,12 @@ +package org_idp_gitlab_self_hosted_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils/org_idp_test_utils" +) + +func TestAccOrgIdPGitLabSelfHosted(t *testing.T) { + org_idp_test_utils.RunOrgLifecyleTest(t, "zitadel_org_idp_gitlab_self_hosted", idp_utils.ClientSecretVar) +} diff --git a/zitadel/org_idp_google/datasource.go b/zitadel/org_idp_google/datasource.go new file mode 100644 index 00000000..4a7feeff --- /dev/null +++ b/zitadel/org_idp_google/datasource.go @@ -0,0 +1,27 @@ +package org_idp_google + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a Google IdP of the organization.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + helper.OrgIDVar: helper.OrgIDDatasourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/org_idp_google/funcs.go b/zitadel/org_idp_google/funcs.go new file mode 100644 index 00000000..a009b597 --- /dev/null +++ b/zitadel/org_idp_google/funcs.go @@ -0,0 +1,99 @@ +package org_idp_google + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddGoogleProvider(ctx, &management.AddGoogleProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateGoogleProvider(ctx, &management.UpdateGoogleProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &management.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetGoogle() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + helper.OrgIDVar: idp.GetDetails().GetResourceOwner(), + idp_utils.NameVar: idp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/org_idp_google/org_idp_google_test_dep/dependency.go b/zitadel/org_idp_google/org_idp_google_test_dep/dependency.go new file mode 100644 index 00000000..06a5ba66 --- /dev/null +++ b/zitadel/org_idp_google/org_idp_google_test_dep/dependency.go @@ -0,0 +1,20 @@ +package org_idp_google_test_dep + +import ( + "testing" + + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func Create(t *testing.T, frame *test_utils.InstanceTestFrame) (string, string) { + return test_utils.CreateDefaultDependency(t, "zitadel_idp_google", idp_utils.IdpIDVar, func() (string, error) { + i, err := frame.AddGoogleProvider(frame, &admin.AddGoogleProviderRequest{ + Name: "Google " + frame.UniqueResourcesID, + ClientId: "dummy", + }) + return i.GetId(), err + }) +} diff --git a/zitadel/org_idp_google/resource.go b/zitadel/org_idp_google/resource.go new file mode 100644 index 00000000..219365fb --- /dev/null +++ b/zitadel/org_idp_google/resource.go @@ -0,0 +1,31 @@ +package org_idp_google + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a Google IdP on the organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: org_idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalOrgAndSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/org_idp_google/resource_test.go b/zitadel/org_idp_google/resource_test.go new file mode 100644 index 00000000..222b0933 --- /dev/null +++ b/zitadel/org_idp_google/resource_test.go @@ -0,0 +1,12 @@ +package org_idp_google_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils/org_idp_test_utils" +) + +func TestAccOrgIdPGoogle(t *testing.T) { + org_idp_test_utils.RunOrgLifecyleTest(t, "zitadel_org_idp_google", idp_utils.ClientSecretVar) +} diff --git a/zitadel/org_idp_jwt/const.go b/zitadel/org_idp_jwt/const.go new file mode 100644 index 00000000..7410d1ad --- /dev/null +++ b/zitadel/org_idp_jwt/const.go @@ -0,0 +1,12 @@ +package org_idp_jwt + +const ( + idpIDVar = "idp_id" + nameVar = "name" + stylingTypeVar = "styling_type" + issuerVar = "issuer" + autoRegisterVar = "auto_register" + JwtEndpointVar = "jwt_endpoint" + keysEndpointVar = "keys_endpoint" + headerNameVar = "header_name" +) diff --git a/zitadel/org_idp_jwt/datasource.go b/zitadel/org_idp_jwt/datasource.go new file mode 100644 index 00000000..379e6218 --- /dev/null +++ b/zitadel/org_idp_jwt/datasource.go @@ -0,0 +1,61 @@ +package org_idp_jwt + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a generic JWT IdP on the organization.", + Schema: map[string]*schema.Schema{ + idpIDVar: { + Type: schema.TypeString, + Required: true, + Description: "The ID of this resource.", + }, + helper.OrgIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the organization", + }, + nameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Name of the IDP", + }, + stylingTypeVar: { + Type: schema.TypeString, + Computed: true, + Description: "Some identity providers specify the styling of the button to their login", + }, + JwtEndpointVar: { + Type: schema.TypeString, + Computed: true, + Description: "the endpoint where the jwt can be extracted", + }, + keysEndpointVar: { + Type: schema.TypeString, + Computed: true, + Description: "the endpoint to the key (JWK) which are used to sign the JWT with", + }, + issuerVar: { + Type: schema.TypeString, + Computed: true, + Description: "the issuer of the jwt (for validation)", + }, + headerNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "the name of the header where the JWT is sent in, default is authorization", + }, + autoRegisterVar: { + Type: schema.TypeBool, + Computed: true, + Description: "auto register for users from this idp", + }, + }, + ReadContext: read, + } +} diff --git a/zitadel/org_idp_jwt/funcs.go b/zitadel/org_idp_jwt/funcs.go new file mode 100644 index 00000000..2c57295a --- /dev/null +++ b/zitadel/org_idp_jwt/funcs.go @@ -0,0 +1,147 @@ +package org_idp_jwt + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/idp" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveOrgIDP(ctx, &management.RemoveOrgIDPRequest{ + IdpId: d.Id(), + }) + if err != nil { + return diag.Errorf("failed to delete oidc idp: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.AddOrgJWTIDP(ctx, &management.AddOrgJWTIDPRequest{ + Name: d.Get(nameVar).(string), + StylingType: idp.IDPStylingType(idp.IDPStylingType_value[d.Get(stylingTypeVar).(string)]), + JwtEndpoint: d.Get(JwtEndpointVar).(string), + Issuer: d.Get(issuerVar).(string), + KeysEndpoint: d.Get(keysEndpointVar).(string), + HeaderName: d.Get(headerNameVar).(string), + AutoRegister: d.Get(autoRegisterVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to create jwt idp: %v", err) + } + d.SetId(resp.IdpId) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChanges(nameVar, stylingTypeVar, autoRegisterVar) { + _, err := client.UpdateOrgIDP(ctx, &management.UpdateOrgIDPRequest{ + IdpId: d.Id(), + Name: d.Get(nameVar).(string), + StylingType: idp.IDPStylingType(idp.IDPStylingType_value[d.Get(stylingTypeVar).(string)]), + AutoRegister: d.Get(autoRegisterVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to update jwt idp: %v", err) + } + } + + if d.HasChanges(JwtEndpointVar, issuerVar, keysEndpointVar, headerNameVar) { + _, err = client.UpdateOrgIDPJWTConfig(ctx, &management.UpdateOrgIDPJWTConfigRequest{ + IdpId: d.Id(), + JwtEndpoint: d.Get(JwtEndpointVar).(string), + Issuer: d.Get(issuerVar).(string), + KeysEndpoint: d.Get(keysEndpointVar).(string), + HeaderName: d.Get(headerNameVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update jwt idp config: %v", err) + } + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetOrgIDPByID(ctx, &management.GetOrgIDPByIDRequest{Id: helper.GetID(d, idpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get org idp jwt") + } + + idp := resp.GetIdp() + jwt := idp.GetJwtConfig() + set := map[string]interface{}{ + helper.OrgIDVar: idp.GetDetails().ResourceOwner, + nameVar: idp.GetName(), + stylingTypeVar: idp.GetStylingType().String(), + JwtEndpointVar: jwt.GetJwtEndpoint(), + issuerVar: jwt.GetIssuer(), + keysEndpointVar: jwt.GetKeysEndpoint(), + headerNameVar: jwt.GetHeaderName(), + autoRegisterVar: idp.GetAutoRegister(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of jwt idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/org_idp_jwt/resource.go b/zitadel/org_idp_jwt/resource.go new file mode 100644 index 00000000..a8265ea3 --- /dev/null +++ b/zitadel/org_idp_jwt/resource.go @@ -0,0 +1,63 @@ +package org_idp_jwt + +import ( + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/idp" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a generic JWT IdP of the organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + nameVar: { + Type: schema.TypeString, + Required: true, + Description: "Name of the IDP", + }, + stylingTypeVar: { + Type: schema.TypeString, + Required: true, + Description: "Some identity providers specify the styling of the button to their login" + helper.DescriptionEnumValuesList(idp.IDPStylingType_name), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(stylingTypeVar, value, idp.IDPStylingType_value) + }, + }, + JwtEndpointVar: { + Type: schema.TypeString, + Required: true, + Description: "the endpoint where the jwt can be extracted", + }, + keysEndpointVar: { + Type: schema.TypeString, + Required: true, + Description: "the endpoint to the key (JWK) which are used to sign the JWT with", + }, + issuerVar: { + Type: schema.TypeString, + Required: true, + Description: "the issuer of the jwt (for validation)", + }, + headerNameVar: { + Type: schema.TypeString, + Required: true, + Description: "the name of the header where the JWT is sent in, default is authorization", + }, + autoRegisterVar: { + Type: schema.TypeBool, + Required: true, + Description: "auto register for users from this idp", + }, + }, + ReadContext: read, + CreateContext: create, + UpdateContext: update, + DeleteContext: delete, + Importer: helper.ImportWithIDAndOptionalOrg(idp_utils.IdpIDVar), + } +} diff --git a/zitadel/org_idp_jwt/resource_test.go b/zitadel/org_idp_jwt/resource_test.go new file mode 100644 index 00000000..bcba7a44 --- /dev/null +++ b/zitadel/org_idp_jwt/resource_test.go @@ -0,0 +1,58 @@ +package org_idp_jwt_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_jwt" +) + +func TestAccOrgIDPJWT(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_org_idp_jwt") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, org_idp_jwt.JwtEndpointVar, exampleAttributes).AsString() + updatedProperty := "https://example.com/updated" + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + func(configProperty, _ string) string { + return strings.Replace(resourceExample, exampleProperty, configProperty, 1) + }, + exampleProperty, updatedProperty, + "", "", "", + true, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(*frame), updatedProperty), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetOrgIDPByID(frame, &management.GetOrgIDPByIDRequest{ + Id: frame.State(state).ID, + }) + if err != nil { + return err + } + actual := resp.GetIdp().GetJwtConfig().GetJwtEndpoint() + if expect != actual { + return fmt.Errorf("expected jwt endpoint %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/org_idp_ldap/datasource.go b/zitadel/org_idp_ldap/datasource.go new file mode 100644 index 00000000..308b0fd6 --- /dev/null +++ b/zitadel/org_idp_ldap/datasource.go @@ -0,0 +1,49 @@ +package org_idp_ldap + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_ldap" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing an LDAP IdP on the organization.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + helper.OrgIDVar: helper.OrgIDDatasourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + + idp_ldap.ServersVar: idp_ldap.ServersDataSourceField, + idp_ldap.StartTLSVar: idp_ldap.StartTLSDataSourceField, + idp_ldap.BaseDNVar: idp_ldap.BaseDNDataSourceField, + idp_ldap.BindDNVar: idp_ldap.BindDNDataSourceField, + idp_ldap.BindPasswordVar: idp_ldap.BindPasswordDataSourceField, + idp_ldap.UserBaseVar: idp_ldap.UserBaseDataSourceField, + idp_ldap.UserObjectClassesVar: idp_ldap.UserObjectClassesDataSourceField, + idp_ldap.UserFiltersVar: idp_ldap.UserFiltersDataSourceField, + idp_ldap.TimeoutVar: idp_ldap.TimeoutDataSourceField, + idp_ldap.IdAttributeVar: idp_ldap.IdAttributeDataSourceField, + + idp_ldap.FirstNameAttributeVar: idp_ldap.FirstNameAttributeDataSourceField, + idp_ldap.LastNameAttributeVar: idp_ldap.LastNameAttributeDataSourceField, + idp_ldap.DisplayNameAttributeVar: idp_ldap.DisplayNameAttributeDataSourceField, + idp_ldap.NickNameAttributeVar: idp_ldap.NickNameAttributeDataSourceField, + idp_ldap.PreferredUsernameAttributeVar: idp_ldap.PreferredUsernameAttributeDataSourceField, + idp_ldap.EmailAttributeVar: idp_ldap.EmailAttributeDataSourceField, + idp_ldap.EmailVerifiedAttributeVar: idp_ldap.EmailVerifiedAttributeDataSourceField, + idp_ldap.PhoneAttributeVar: idp_ldap.PhoneAttributeDataSourceField, + idp_ldap.PhoneVerifiedAttributeVar: idp_ldap.PhoneVerifiedAttributeDataSourceField, + idp_ldap.PreferredLanguageAttributeVar: idp_ldap.PreferredLanguageAttributeDataSourceField, + idp_ldap.AvatarURLAttributeVar: idp_ldap.AvatarURLAttributeDataSourceField, + idp_ldap.ProfileAttributeVar: idp_ldap.ProfileAttributeDataSourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/org_idp_ldap/funcs.go b/zitadel/org_idp_ldap/funcs.go new file mode 100644 index 00000000..55965a1b --- /dev/null +++ b/zitadel/org_idp_ldap/funcs.go @@ -0,0 +1,179 @@ +package org_idp_ldap + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/idp" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_ldap" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + timeout, err := time.ParseDuration(d.Get(idp_ldap.TimeoutVar).(string)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddLDAPProvider(ctx, &management.AddLDAPProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + + Servers: idp_utils.InterfaceToStringSlice(d.Get(idp_ldap.ServersVar)), + StartTls: idp_utils.BoolValue(d, idp_ldap.StartTLSVar), + BaseDn: idp_utils.StringValue(d, idp_ldap.BaseDNVar), + BindDn: idp_utils.StringValue(d, idp_ldap.BindDNVar), + BindPassword: idp_utils.StringValue(d, idp_ldap.BindPasswordVar), + UserBase: idp_utils.StringValue(d, idp_ldap.UserBaseVar), + UserObjectClasses: helper.GetOkSetToStringSlice(d, idp_ldap.UserObjectClassesVar), + UserFilters: helper.GetOkSetToStringSlice(d, idp_ldap.UserFiltersVar), + Timeout: durationpb.New(timeout), + + Attributes: &idp.LDAPAttributes{ + IdAttribute: idp_utils.StringValue(d, idp_ldap.IdAttributeVar), + FirstNameAttribute: idp_utils.StringValue(d, idp_ldap.FirstNameAttributeVar), + LastNameAttribute: idp_utils.StringValue(d, idp_ldap.LastNameAttributeVar), + DisplayNameAttribute: idp_utils.StringValue(d, idp_ldap.DisplayNameAttributeVar), + NickNameAttribute: idp_utils.StringValue(d, idp_ldap.NickNameAttributeVar), + PreferredUsernameAttribute: idp_utils.StringValue(d, idp_ldap.PreferredUsernameAttributeVar), + EmailAttribute: idp_utils.StringValue(d, idp_ldap.EmailAttributeVar), + EmailVerifiedAttribute: idp_utils.StringValue(d, idp_ldap.EmailVerifiedAttributeVar), + PhoneAttribute: idp_utils.StringValue(d, idp_ldap.PhoneAttributeVar), + PhoneVerifiedAttribute: idp_utils.StringValue(d, idp_ldap.PhoneVerifiedAttributeVar), + PreferredLanguageAttribute: idp_utils.StringValue(d, idp_ldap.PreferredLanguageAttributeVar), + AvatarUrlAttribute: idp_utils.StringValue(d, idp_ldap.AvatarURLAttributeVar), + ProfileAttribute: idp_utils.StringValue(d, idp_ldap.ProfileAttributeVar), + }, + }) + if err != nil { + return diag.Errorf("failed to create idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + timeout, err := time.ParseDuration(d.Get(idp_ldap.TimeoutVar).(string)) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateLDAPProvider(ctx, &management.UpdateLDAPProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + + Servers: idp_utils.InterfaceToStringSlice(d.Get(idp_ldap.ServersVar)), + StartTls: idp_utils.BoolValue(d, idp_ldap.StartTLSVar), + BaseDn: idp_utils.StringValue(d, idp_ldap.BaseDNVar), + BindDn: idp_utils.StringValue(d, idp_ldap.BindDNVar), + BindPassword: idp_utils.StringValue(d, idp_ldap.BindPasswordVar), + UserBase: idp_utils.StringValue(d, idp_ldap.UserBaseVar), + UserObjectClasses: helper.GetOkSetToStringSlice(d, idp_ldap.UserObjectClassesVar), + UserFilters: helper.GetOkSetToStringSlice(d, idp_ldap.UserFiltersVar), + Timeout: durationpb.New(timeout), + + Attributes: &idp.LDAPAttributes{ + IdAttribute: idp_utils.StringValue(d, idp_ldap.IdAttributeVar), + FirstNameAttribute: idp_utils.StringValue(d, idp_ldap.FirstNameAttributeVar), + LastNameAttribute: idp_utils.StringValue(d, idp_ldap.LastNameAttributeVar), + DisplayNameAttribute: idp_utils.StringValue(d, idp_ldap.DisplayNameAttributeVar), + NickNameAttribute: idp_utils.StringValue(d, idp_ldap.NickNameAttributeVar), + PreferredUsernameAttribute: idp_utils.StringValue(d, idp_ldap.PreferredUsernameAttributeVar), + EmailAttribute: idp_utils.StringValue(d, idp_ldap.EmailAttributeVar), + EmailVerifiedAttribute: idp_utils.StringValue(d, idp_ldap.EmailVerifiedAttributeVar), + PhoneAttribute: idp_utils.StringValue(d, idp_ldap.PhoneAttributeVar), + PhoneVerifiedAttribute: idp_utils.StringValue(d, idp_ldap.PhoneVerifiedAttributeVar), + PreferredLanguageAttribute: idp_utils.StringValue(d, idp_ldap.PreferredLanguageAttributeVar), + AvatarUrlAttribute: idp_utils.StringValue(d, idp_ldap.AvatarURLAttributeVar), + ProfileAttribute: idp_utils.StringValue(d, idp_ldap.ProfileAttributeVar), + }, + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &management.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetLdap() + attributesCfg := specificCfg.GetAttributes() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + helper.OrgIDVar: idp.GetDetails().GetResourceOwner(), + idp_utils.NameVar: idp.GetName(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + + idp_ldap.ServersVar: specificCfg.GetServers(), + idp_ldap.StartTLSVar: specificCfg.GetStartTls(), + idp_ldap.BaseDNVar: specificCfg.GetBaseDn(), + idp_ldap.BindDNVar: specificCfg.GetBindDn(), + idp_ldap.BindPasswordVar: idp_utils.StringValue(d, idp_ldap.BindPasswordVar), + idp_ldap.UserBaseVar: specificCfg.GetUserBase(), + idp_ldap.UserObjectClassesVar: specificCfg.GetUserObjectClasses(), + idp_ldap.UserFiltersVar: specificCfg.GetUserFilters(), + idp_ldap.TimeoutVar: specificCfg.GetTimeout().AsDuration().String(), + idp_ldap.IdAttributeVar: attributesCfg.GetIdAttribute(), + + idp_ldap.FirstNameAttributeVar: attributesCfg.GetFirstNameAttribute(), + idp_ldap.LastNameAttributeVar: attributesCfg.GetLastNameAttribute(), + idp_ldap.DisplayNameAttributeVar: attributesCfg.GetDisplayNameAttribute(), + idp_ldap.NickNameAttributeVar: attributesCfg.GetNickNameAttribute(), + idp_ldap.PreferredUsernameAttributeVar: attributesCfg.GetPreferredUsernameAttribute(), + idp_ldap.EmailAttributeVar: attributesCfg.GetEmailAttribute(), + idp_ldap.EmailVerifiedAttributeVar: attributesCfg.GetEmailVerifiedAttribute(), + idp_ldap.PhoneAttributeVar: attributesCfg.GetPhoneAttribute(), + idp_ldap.PhoneVerifiedAttributeVar: attributesCfg.GetPhoneVerifiedAttribute(), + idp_ldap.PreferredLanguageAttributeVar: attributesCfg.GetPreferredLanguageAttribute(), + idp_ldap.AvatarURLAttributeVar: attributesCfg.GetAvatarUrlAttribute(), + idp_ldap.ProfileAttributeVar: attributesCfg.GetProfileAttribute(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/org_idp_ldap/resource.go b/zitadel/org_idp_ldap/resource.go new file mode 100644 index 00000000..d140d1ce --- /dev/null +++ b/zitadel/org_idp_ldap/resource.go @@ -0,0 +1,53 @@ +package org_idp_ldap + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_ldap" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing an LDAP IdP on the organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + + idp_ldap.ServersVar: idp_ldap.ServersResourceField, + idp_ldap.StartTLSVar: idp_ldap.StartTLSResourceField, + idp_ldap.BaseDNVar: idp_ldap.BaseDNResourceField, + idp_ldap.BindDNVar: idp_ldap.BindDNResourceField, + idp_ldap.BindPasswordVar: idp_ldap.BindPasswordResourceField, + idp_ldap.UserBaseVar: idp_ldap.UserBaseResourceField, + idp_ldap.UserObjectClassesVar: idp_ldap.UserObjectClassesResourceField, + idp_ldap.UserFiltersVar: idp_ldap.UserFiltersResourceField, + idp_ldap.TimeoutVar: idp_ldap.TimeoutResourceField, + idp_ldap.IdAttributeVar: idp_ldap.IdAttributeResourceField, + + idp_ldap.FirstNameAttributeVar: idp_ldap.FirstNameAttributeResourceField, + idp_ldap.LastNameAttributeVar: idp_ldap.LastNameAttributeResourceField, + idp_ldap.DisplayNameAttributeVar: idp_ldap.DisplayNameAttributeResourceField, + idp_ldap.NickNameAttributeVar: idp_ldap.NickNameAttributeResourceField, + idp_ldap.PreferredUsernameAttributeVar: idp_ldap.PreferredUsernameAttributeResourceField, + idp_ldap.EmailAttributeVar: idp_ldap.EmailAttributeResourceField, + idp_ldap.EmailVerifiedAttributeVar: idp_ldap.EmailVerifiedAttributeResourceField, + idp_ldap.PhoneAttributeVar: idp_ldap.PhoneAttributeResourceField, + idp_ldap.PhoneVerifiedAttributeVar: idp_ldap.PhoneVerifiedAttributeResourceField, + idp_ldap.PreferredLanguageAttributeVar: idp_ldap.PreferredLanguageAttributeResourceField, + idp_ldap.AvatarURLAttributeVar: idp_ldap.AvatarURLAttributeResourceField, + idp_ldap.ProfileAttributeVar: idp_ldap.ProfileAttributeResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: org_idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalOrgAndSecret(idp_utils.IdpIDVar, idp_ldap.BindPasswordVar), + } +} diff --git a/zitadel/org_idp_ldap/resource_test.go b/zitadel/org_idp_ldap/resource_test.go new file mode 100644 index 00000000..9562ee77 --- /dev/null +++ b/zitadel/org_idp_ldap/resource_test.go @@ -0,0 +1,12 @@ +package org_idp_ldap_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_ldap" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils/org_idp_test_utils" +) + +func TestAccOrgIdPLDAP(t *testing.T) { + org_idp_test_utils.RunOrgLifecyleTest(t, "zitadel_org_idp_ldap", idp_ldap.BindPasswordVar) +} diff --git a/zitadel/org_idp_oidc/datasource.go b/zitadel/org_idp_oidc/datasource.go new file mode 100644 index 00000000..8b00dca2 --- /dev/null +++ b/zitadel/org_idp_oidc/datasource.go @@ -0,0 +1,29 @@ +package org_idp_oidc + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing a generic OIDC IdP on the organization.", + Schema: map[string]*schema.Schema{ + idp_utils.IdpIDVar: idp_utils.IdPIDDataSourceField, + helper.OrgIDVar: helper.OrgIDDatasourceField, + idp_utils.NameVar: idp_utils.NameDataSourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDDataSourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretDataSourceField, + idp_utils.ScopesVar: idp_utils.ScopesDataSourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedDataSourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedDataSourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationDataSourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateDataSourceField, + IssuerVar: IssuerDatasourceField, + IsIdTokenMappingVar: IsIdTokenMappingDatasourceField, + }, + ReadContext: read, + } +} diff --git a/zitadel/org_idp_oidc/funcs.go b/zitadel/org_idp_oidc/funcs.go new file mode 100644 index 00000000..7fb1ad76 --- /dev/null +++ b/zitadel/org_idp_oidc/funcs.go @@ -0,0 +1,105 @@ +package org_idp_oidc + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.AddGenericOIDCProvider(ctx, &management.AddGenericOIDCProviderRequest{ + Name: idp_utils.StringValue(d, idp_utils.NameVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + Issuer: idp_utils.StringValue(d, IssuerVar), + IsIdTokenMapping: idp_utils.BoolValue(d, IsIdTokenMappingVar), + }) + if err != nil { + return diag.Errorf("failed to create oidc idp: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, idp_utils.StringValue(d, helper.OrgIDVar)) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateGenericOIDCProvider(ctx, &management.UpdateGenericOIDCProviderRequest{ + Id: d.Id(), + Name: idp_utils.StringValue(d, idp_utils.NameVar), + Issuer: idp_utils.StringValue(d, IssuerVar), + ClientId: idp_utils.StringValue(d, idp_utils.ClientIDVar), + ClientSecret: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + Scopes: idp_utils.ScopesValue(d), + ProviderOptions: idp_utils.ProviderOptionsValue(d), + IsIdTokenMapping: idp_utils.BoolValue(d, IsIdTokenMappingVar), + }) + if err != nil { + return diag.Errorf("failed to update idp: %v", err) + } + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetProviderByID(ctx, &management.GetProviderByIDRequest{Id: helper.GetID(d, idp_utils.IdpIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get idp") + } + idp := resp.GetIdp() + cfg := idp.GetConfig() + specificCfg := cfg.GetOidc() + generalCfg := cfg.GetOptions() + set := map[string]interface{}{ + helper.OrgIDVar: idp.GetDetails().GetResourceOwner(), + idp_utils.NameVar: idp.GetName(), + idp_utils.ClientIDVar: specificCfg.GetClientId(), + idp_utils.ClientSecretVar: idp_utils.StringValue(d, idp_utils.ClientSecretVar), + idp_utils.ScopesVar: specificCfg.GetScopes(), + idp_utils.IsLinkingAllowedVar: generalCfg.GetIsLinkingAllowed(), + idp_utils.IsCreationAllowedVar: generalCfg.GetIsCreationAllowed(), + idp_utils.IsAutoCreationVar: generalCfg.GetIsAutoCreation(), + idp_utils.IsAutoUpdateVar: generalCfg.GetIsAutoUpdate(), + IssuerVar: specificCfg.GetIssuer(), + IsIdTokenMappingVar: specificCfg.GetIsIdTokenMapping(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of oidc idp: %v", k, err) + } + } + d.SetId(idp.Id) + return nil +} diff --git a/zitadel/org_idp_oidc/resource.go b/zitadel/org_idp_oidc/resource.go new file mode 100644 index 00000000..d3b6f87c --- /dev/null +++ b/zitadel/org_idp_oidc/resource.go @@ -0,0 +1,33 @@ +package org_idp_oidc + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a generic OIDC IdP on the organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + idp_utils.NameVar: idp_utils.NameResourceField, + idp_utils.ClientIDVar: idp_utils.ClientIDResourceField, + idp_utils.ClientSecretVar: idp_utils.ClientSecretResourceField, + idp_utils.ScopesVar: idp_utils.ScopesResourceField, + idp_utils.IsLinkingAllowedVar: idp_utils.IsLinkingAllowedResourceField, + idp_utils.IsCreationAllowedVar: idp_utils.IsCreationAllowedResourceField, + idp_utils.IsAutoCreationVar: idp_utils.IsAutoCreationResourceField, + idp_utils.IsAutoUpdateVar: idp_utils.IsAutoUpdateResourceField, + IssuerVar: IssuerResourceField, + IsIdTokenMappingVar: IsIdTokenMappingResourceField, + }, + ReadContext: read, + UpdateContext: update, + CreateContext: create, + DeleteContext: org_idp_utils.Delete, + Importer: helper.ImportWithIDAndOptionalOrgAndSecret(idp_utils.IdpIDVar, idp_utils.ClientSecretVar), + } +} diff --git a/zitadel/org_idp_oidc/resource_test.go b/zitadel/org_idp_oidc/resource_test.go new file mode 100644 index 00000000..978fc490 --- /dev/null +++ b/zitadel/org_idp_oidc/resource_test.go @@ -0,0 +1,12 @@ +package org_idp_oidc_test + +import ( + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_utils/org_idp_test_utils" +) + +func TestAccOrgIDPOIDC(t *testing.T) { + org_idp_test_utils.RunOrgLifecyleTest(t, "zitadel_org_idp_oidc", idp_utils.ClientSecretVar) +} diff --git a/zitadel/org_idp_oidc/schema.go b/zitadel/org_idp_oidc/schema.go new file mode 100644 index 00000000..ea4231fd --- /dev/null +++ b/zitadel/org_idp_oidc/schema.go @@ -0,0 +1,31 @@ +package org_idp_oidc + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +const ( + IsIdTokenMappingVar = "is_id_token_mapping" + IssuerVar = "issuer" +) + +var ( + IsIdTokenMappingResourceField = &schema.Schema{ + Type: schema.TypeBool, + Required: true, + Description: "if true, provider information get mapped from the id token, not from the userinfo endpoint", + } + IsIdTokenMappingDatasourceField = &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + Description: "if true, provider information get mapped from the id token, not from the userinfo endpoint.", + } + IssuerResourceField = &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "the issuer of the idp", + } + IssuerDatasourceField = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "the issuer of the idp", + } +) diff --git a/zitadel/org_idp_utils/funcs.go b/zitadel/org_idp_utils/funcs.go new file mode 100644 index 00000000..03d41a10 --- /dev/null +++ b/zitadel/org_idp_utils/funcs.go @@ -0,0 +1,27 @@ +package org_idp_utils + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func Delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + _, err = client.DeleteProvider(ctx, &management.DeleteProviderRequest{Id: d.Id()}) + if err != nil { + return diag.Errorf("failed to delete idp: %v", err) + } + return nil +} diff --git a/zitadel/org_idp_utils/org_idp_test_utils/checks.go b/zitadel/org_idp_utils/org_idp_test_utils/checks.go new file mode 100644 index 00000000..f76df16a --- /dev/null +++ b/zitadel/org_idp_utils/org_idp_test_utils/checks.go @@ -0,0 +1,39 @@ +package org_idp_test_utils + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func CheckCreationAllowed(frame test_utils.OrgTestFrame) func(bool) resource.TestCheckFunc { + return func(expectAllowed bool) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteProvider, err := frame.GetProviderByID(frame, &management.GetProviderByIDRequest{Id: frame.State(state).ID}) + if err != nil { + return err + } + actual := remoteProvider.GetIdp().GetConfig().GetOptions().GetIsCreationAllowed() + if actual != expectAllowed { + return fmt.Errorf("expected creation allowed to be %t, but got %t", expectAllowed, actual) + } + return nil + } + } +} + +func CheckDestroy(frame test_utils.OrgTestFrame) resource.TestCheckFunc { + return func(state *terraform.State) error { + err := CheckCreationAllowed(frame)(false)(state) + if status.Code(err) != codes.NotFound { + return fmt.Errorf("expected not found error but got: %w", err) + } + return nil + } +} diff --git a/zitadel/org_idp_utils/org_idp_test_utils/lifecyletest.go b/zitadel/org_idp_utils/org_idp_test_utils/lifecyletest.go new file mode 100644 index 00000000..32304f2b --- /dev/null +++ b/zitadel/org_idp_utils/org_idp_test_utils/lifecyletest.go @@ -0,0 +1,37 @@ +package org_idp_test_utils + +import ( + "strings" + "testing" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_utils" +) + +func RunOrgLifecyleTest(t *testing.T, resourceName, secretAttribute string) { + frame := test_utils.NewOrgTestFrame(t, resourceName) + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + nameProperty := test_utils.AttributeValue(t, idp_utils.NameVar, exampleAttributes).AsString() + // Using a unique name makes the test idempotent on failures + resourceExample = strings.Replace(resourceExample, nameProperty, frame.UniqueResourcesID, 1) + exampleProperty := test_utils.AttributeValue(t, idp_utils.IsCreationAllowedVar, exampleAttributes).True() + exampleSecret := test_utils.AttributeValue(t, secretAttribute, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, exampleSecret), + true, false, + secretAttribute, exampleSecret, "an_updated_secret", + false, + CheckCreationAllowed(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + CheckDestroy(*frame), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportOrgId(frame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, secretAttribute), + ), + ) +} diff --git a/zitadel/org_member/const.go b/zitadel/org_member/const.go new file mode 100644 index 00000000..18e909f8 --- /dev/null +++ b/zitadel/org_member/const.go @@ -0,0 +1,6 @@ +package org_member + +const ( + UserIDVar = "user_id" + RolesVar = "roles" +) diff --git a/zitadel/org_member/funcs.go b/zitadel/org_member/funcs.go new file mode 100644 index 00000000..8e8ecd7f --- /dev/null +++ b/zitadel/org_member/funcs.go @@ -0,0 +1,145 @@ +package org_member + +import ( + "context" + "strings" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/member" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveOrgMember(ctx, &management.RemoveOrgMemberRequest{ + UserId: d.Get(UserIDVar).(string), + }) + if err != nil { + return diag.Errorf("failed to delete orgmember: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateOrgMember(ctx, &management.UpdateOrgMemberRequest{ + UserId: d.Get(UserIDVar).(string), + Roles: helper.GetOkSetToStringSlice(d, RolesVar), + }) + if err != nil { + return diag.Errorf("failed to update orgmember: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + userID := d.Get(UserIDVar).(string) + _, err = client.AddOrgMember(ctx, &management.AddOrgMemberRequest{ + UserId: userID, + Roles: helper.GetOkSetToStringSlice(d, RolesVar), + }) + if err != nil { + return diag.Errorf("failed to create orgmember: %v", err) + } + d.SetId(getOrgMemberID(org, userID)) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + userID := d.Get(UserIDVar).(string) + resp, err := client.ListOrgMembers(ctx, &management.ListOrgMembersRequest{ + Queries: []*member.SearchQuery{{ + Query: &member.SearchQuery_UserIdQuery{ + UserIdQuery: &member.UserIDQuery{ + UserId: userID, + }, + }, + }}, + }) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to list org members") + } + + if len(resp.Result) == 1 { + orgMember := resp.Result[0] + set := map[string]interface{}{ + UserIDVar: orgMember.GetUserId(), + helper.OrgIDVar: orgMember.GetDetails().GetResourceOwner(), + RolesVar: orgMember.GetRoles(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of orgmember: %v", k, err) + } + } + d.SetId(getOrgMemberID(org, userID)) + return nil + } + + d.SetId("") + return nil +} + +func getOrgMemberID(org string, userID string) string { + return org + "_" + userID +} + +func splitOrgMemberID(orgMemberID string) (string, string) { + parts := strings.Split(orgMemberID, "_") + return parts[0], parts[1] +} diff --git a/zitadel/org_member/resource.go b/zitadel/org_member/resource.go new file mode 100644 index 00000000..ae5de7b1 --- /dev/null +++ b/zitadel/org_member/resource.go @@ -0,0 +1,38 @@ +package org_member + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the membership of a user on an organization, defined with the given role.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + UserIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the user", + ForceNew: true, + }, + RolesVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + Description: "List of roles granted", + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithEmptyID( + helper.NewImportAttribute(UserIDVar, helper.ConvertID, false), + helper.ImportOptionalOrgAttribute, + ), + } +} diff --git a/zitadel/org_member/resource_test.go b/zitadel/org_member/resource_test.go new file mode 100644 index 00000000..e4883c3a --- /dev/null +++ b/zitadel/org_member/resource_test.go @@ -0,0 +1,64 @@ +package org_member_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/member" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/human_user/human_user_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_member" +) + +func TestAccOrgMember(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_org_member") + userDep, userID := human_user_test_dep.Create(t, frame) + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, org_member.RolesVar, exampleAttributes).AsValueSlice()[0].AsString() + updatedProperty := "ORG_OWNER_VIEWER" + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, userDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + true, + checkRemoteProperty(*frame, userID), + regexp.MustCompile(fmt.Sprintf("^%s_%s$", helper.ZitadelGeneratedIdPattern, helper.ZitadelGeneratedIdPattern)), + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(*frame, userID), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportStateAttribute(frame.BaseTestFrame, org_member.UserIDVar), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame, userID string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.ListOrgMembers(frame, &management.ListOrgMembersRequest{ + Queries: []*member.SearchQuery{{ + Query: &member.SearchQuery_UserIdQuery{UserIdQuery: &member.UserIDQuery{UserId: userID}}, + }}, + }) + if err != nil { + return err + } + if len(resp.Result) == 0 || len(resp.Result[0].Roles) == 0 { + return fmt.Errorf("expected 1 user with 1 role, but got %d: %w", len(resp.Result), test_utils.ErrNotFound) + } + actual := resp.Result[0].Roles[0] + if expect != actual { + return fmt.Errorf("expected role %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/password_change_message_text/resource.go b/zitadel/password_change_message_text/resource.go new file mode 100644 index 00000000..3e5938fc --- /dev/null +++ b/zitadel/password_change_message_text/resource.go @@ -0,0 +1,251 @@ +package password_change_message_text + +import ( + "context" + "strings" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &passwordChangeMessageTextResource{} +) + +func New() resource.Resource { + return &passwordChangeMessageTextResource{} +} + +type passwordChangeMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *passwordChangeMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_password_change_message_text" +} + +func (r *passwordChangeMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return text.GenSchemaMessageCustomText(ctx) +} + +func (r *passwordChangeMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *passwordChangeMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomPasswordChangeMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomPasswordChangeMessageCustomText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *passwordChangeMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + orgID, language := getID(ctx, state) + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomPasswordChangeMessageText(ctx, &management.GetCustomPasswordChangeMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *passwordChangeMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomPasswordChangeMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomPasswordChangeMessageCustomText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *passwordChangeMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + orgID, language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomPasswordChangeMessageTextToDefault(ctx, &management.ResetCustomPasswordChangeMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, orgID string, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(orgID + "_" + language) + attrs[helper.OrgIDVar] = types.StringValue(orgID) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) (string, string) { + id := helper.GetStringFromAttr(ctx, obj.Attributes(), "id") + parts := strings.Split(id, "_") + if len(parts) == 2 { + return parts[0], parts[1] + } + return helper.GetStringFromAttr(ctx, obj.Attributes(), helper.OrgIDVar), helper.GetStringFromAttr(ctx, obj.Attributes(), LanguageVar) +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(plan.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(state.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} diff --git a/zitadel/password_change_message_text/resource_test.go b/zitadel/password_change_message_text/resource_test.go new file mode 100644 index 00000000..6198c4b0 --- /dev/null +++ b/zitadel/password_change_message_text/resource_test.go @@ -0,0 +1,52 @@ +package password_change_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/password_change_message_text" +) + +func TestAccPasswordChangeMessageText(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_password_change_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + updatedProperty := "updatedtitle" + exampleLanguage := test_utils.AttributeValue(t, password_change_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^\d{18}_%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("ZITADEL - Password of user has changed"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomPasswordChangeMessageText(frame, &management.GetCustomPasswordChangeMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/password_complexity_policy/const.go b/zitadel/password_complexity_policy/const.go new file mode 100644 index 00000000..b9547df3 --- /dev/null +++ b/zitadel/password_complexity_policy/const.go @@ -0,0 +1,9 @@ +package password_complexity_policy + +const ( + minLengthVar = "min_length" + hasUppercaseVar = "has_uppercase" + hasLowercaseVar = "has_lowercase" + hasNumberVar = "has_number" + hasSymbolVar = "has_symbol" +) diff --git a/zitadel/password_complexity_policy/funcs.go b/zitadel/password_complexity_policy/funcs.go new file mode 100644 index 00000000..a57e8f2e --- /dev/null +++ b/zitadel/password_complexity_policy/funcs.go @@ -0,0 +1,120 @@ +package password_complexity_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + _, err = client.ResetPasswordComplexityPolicyToDefault(ctx, &management.ResetPasswordComplexityPolicyToDefaultRequest{}) + if err != nil { + return diag.Errorf("failed to reset password complexity policy: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + _, err = client.UpdateCustomPasswordComplexityPolicy(ctx, &management.UpdateCustomPasswordComplexityPolicyRequest{ + MinLength: uint64(d.Get(minLengthVar).(int)), + HasUppercase: d.Get(hasUppercaseVar).(bool), + HasLowercase: d.Get(hasLowercaseVar).(bool), + HasNumber: d.Get(hasNumberVar).(bool), + HasSymbol: d.Get(hasSymbolVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to update password complexity policy: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + _, err = client.AddCustomPasswordComplexityPolicy(ctx, &management.AddCustomPasswordComplexityPolicyRequest{ + MinLength: uint64(d.Get(minLengthVar).(int)), + HasUppercase: d.Get(hasUppercaseVar).(bool), + HasLowercase: d.Get(hasLowercaseVar).(bool), + HasNumber: d.Get(hasNumberVar).(bool), + HasSymbol: d.Get(hasSymbolVar).(bool), + }) + if err != nil { + return diag.Errorf("failed to create password complexity policy: %v", err) + } + d.SetId(org) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := helper.GetID(d, helper.OrgIDVar) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetPasswordComplexityPolicy(ctx, &management.GetPasswordComplexityPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get password complexity policy") + } + policy := resp.Policy + if policy.GetIsDefault() == true { + d.SetId("") + return nil + } + set := map[string]interface{}{ + helper.OrgIDVar: policy.GetDetails().GetResourceOwner(), + minLengthVar: policy.GetMinLength(), + hasUppercaseVar: policy.GetHasUppercase(), + hasLowercaseVar: policy.GetHasLowercase(), + hasNumberVar: policy.GetHasNumber(), + hasSymbolVar: policy.GetHasSymbol(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of password complexity policy: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/password_complexity_policy/resource.go b/zitadel/password_complexity_policy/resource.go new file mode 100644 index 00000000..af3161df --- /dev/null +++ b/zitadel/password_complexity_policy/resource.go @@ -0,0 +1,46 @@ +package password_complexity_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the custom password complexity policy of an organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + minLengthVar: { + Type: schema.TypeInt, + Required: true, + Description: "Minimal length for the password", + }, + hasUppercaseVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if the password MUST contain an upper case letter", + }, + hasLowercaseVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if the password MUST contain a lower case letter", + }, + hasNumberVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if the password MUST contain a number", + }, + hasSymbolVar: { + Type: schema.TypeBool, + Required: true, + Description: "defines if the password MUST contain a symbol. E.g. \"$\"", + }, + }, + DeleteContext: delete, + ReadContext: read, + CreateContext: create, + UpdateContext: update, + Importer: helper.ImportWithOptionalOrg(), + } +} diff --git a/zitadel/password_complexity_policy/resource_test.go b/zitadel/password_complexity_policy/resource_test.go new file mode 100644 index 00000000..73be9299 --- /dev/null +++ b/zitadel/password_complexity_policy/resource_test.go @@ -0,0 +1,53 @@ +package password_complexity_policy_test + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func TestAccPasswordComplexityPolicy(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_password_complexity_policy") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty, err := strconv.ParseUint(test_utils.AttributeValue(t, "min_length", exampleAttributes).AsString(), 10, 64) + if err != nil { + t.Fatalf("could not parse example property: %v", err) + } + updatedProperty := uint64(10) + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + checkRemoteProperty(*frame)(exampleProperty), + test_utils.ImportOrgId(frame), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame) func(uint64) resource.TestCheckFunc { + return func(expect uint64) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetPasswordComplexityPolicy(frame, &management.GetPasswordComplexityPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetMinLength() + if actual != expect { + return fmt.Errorf("expected %d, but got %d", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/password_reset_message_text/resource.go b/zitadel/password_reset_message_text/resource.go new file mode 100644 index 00000000..a80f857e --- /dev/null +++ b/zitadel/password_reset_message_text/resource.go @@ -0,0 +1,251 @@ +package password_reset_message_text + +import ( + "context" + "strings" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &passwordResetMessageTextResource{} +) + +func New() resource.Resource { + return &passwordResetMessageTextResource{} +} + +type passwordResetMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *passwordResetMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_password_reset_message_text" +} + +func (r *passwordResetMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return text.GenSchemaMessageCustomText(ctx) +} + +func (r *passwordResetMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *passwordResetMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomPasswordResetMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomPasswordResetMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *passwordResetMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + orgID, language := getID(ctx, state) + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomPasswordResetMessageText(ctx, &management.GetCustomPasswordResetMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *passwordResetMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomPasswordResetMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomPasswordResetMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *passwordResetMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + orgID, language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomPasswordResetMessageTextToDefault(ctx, &management.ResetCustomPasswordResetMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, orgID string, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(orgID + "_" + language) + attrs[helper.OrgIDVar] = types.StringValue(orgID) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) (string, string) { + id := helper.GetStringFromAttr(ctx, obj.Attributes(), "id") + parts := strings.Split(id, "_") + if len(parts) == 2 { + return parts[0], parts[1] + } + return helper.GetStringFromAttr(ctx, obj.Attributes(), helper.OrgIDVar), helper.GetStringFromAttr(ctx, obj.Attributes(), LanguageVar) +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(plan.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(state.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} diff --git a/zitadel/password_reset_message_text/resource_test.go b/zitadel/password_reset_message_text/resource_test.go new file mode 100644 index 00000000..785c65cf --- /dev/null +++ b/zitadel/password_reset_message_text/resource_test.go @@ -0,0 +1,52 @@ +package password_reset_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/password_reset_message_text" +) + +func TestAccPasswordResetMessageText(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_password_reset_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + updatedProperty := "updatedtitle" + exampleLanguage := test_utils.AttributeValue(t, password_reset_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^\d{18}_%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("Zitadel - Reset password"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomPasswordResetMessageText(frame, &management.GetCustomPasswordResetMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/passwordless_registration_message_text/resource.go b/zitadel/passwordless_registration_message_text/resource.go new file mode 100644 index 00000000..0812baa5 --- /dev/null +++ b/zitadel/passwordless_registration_message_text/resource.go @@ -0,0 +1,251 @@ +package passwordless_registration_message_text + +import ( + "context" + "strings" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &passwordlessRegistrationMessageTextResource{} +) + +func New() resource.Resource { + return &passwordlessRegistrationMessageTextResource{} +} + +type passwordlessRegistrationMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *passwordlessRegistrationMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_passwordless_registration_message_text" +} + +func (r *passwordlessRegistrationMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return text.GenSchemaMessageCustomText(ctx) +} + +func (r *passwordlessRegistrationMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *passwordlessRegistrationMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomPasswordlessRegistrationMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomPasswordlessRegistrationMessageCustomText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *passwordlessRegistrationMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + orgID, language := getID(ctx, state) + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomPasswordlessRegistrationMessageText(ctx, &management.GetCustomPasswordlessRegistrationMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *passwordlessRegistrationMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomPasswordlessRegistrationMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomPasswordlessRegistrationMessageCustomText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *passwordlessRegistrationMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + orgID, language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomPasswordlessRegistrationMessageTextToDefault(ctx, &management.ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, orgID string, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(orgID + "_" + language) + attrs[helper.OrgIDVar] = types.StringValue(orgID) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) (string, string) { + id := helper.GetStringFromAttr(ctx, obj.Attributes(), "id") + parts := strings.Split(id, "_") + if len(parts) == 2 { + return parts[0], parts[1] + } + return helper.GetStringFromAttr(ctx, obj.Attributes(), helper.OrgIDVar), helper.GetStringFromAttr(ctx, obj.Attributes(), LanguageVar) +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(plan.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(state.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} diff --git a/zitadel/passwordless_registration_message_text/resource_test.go b/zitadel/passwordless_registration_message_text/resource_test.go new file mode 100644 index 00000000..6433b094 --- /dev/null +++ b/zitadel/passwordless_registration_message_text/resource_test.go @@ -0,0 +1,52 @@ +package passwordless_registration_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/passwordless_registration_message_text" +) + +func TestAccPasswordlessRegistrationMessageText(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_passwordless_registration_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + updatedProperty := "updatedtitle" + exampleLanguage := test_utils.AttributeValue(t, passwordless_registration_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^\d{18}_%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("ZITADEL - Add Passwordless Login"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomPasswordlessRegistrationMessageText(frame, &management.GetCustomPasswordlessRegistrationMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/pat/const.go b/zitadel/pat/const.go new file mode 100644 index 00000000..e637b49a --- /dev/null +++ b/zitadel/pat/const.go @@ -0,0 +1,8 @@ +package pat + +const ( + tokenIDVar = "token_id" + UserIDVar = "user_id" + TokenVar = "token" + ExpirationDateVar = "expiration_date" +) diff --git a/zitadel/pat/funcs.go b/zitadel/pat/funcs.go new file mode 100644 index 00000000..70b65231 --- /dev/null +++ b/zitadel/pat/funcs.go @@ -0,0 +1,114 @@ +package pat + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemovePersonalAccessToken(ctx, &management.RemovePersonalAccessTokenRequest{ + UserId: d.Get(UserIDVar).(string), + TokenId: d.Id(), + }) + if err != nil { + return diag.Errorf("failed to delete PAT: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + orgID := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, orgID) + if err != nil { + return diag.FromErr(err) + } + + req := &management.AddPersonalAccessTokenRequest{ + UserId: d.Get(UserIDVar).(string), + } + if expiration, ok := d.GetOk(ExpirationDateVar); ok { + t, err := time.Parse(time.RFC3339, expiration.(string)) + if err != nil { + return diag.Errorf("failed to parse time: %v", err) + } + req.ExpirationDate = timestamppb.New(t) + } + + resp, err := client.AddPersonalAccessToken(ctx, req) + if err != nil { + return diag.FromErr(err) + } + + if err := d.Set(TokenVar, resp.GetToken()); err != nil { + return diag.FromErr(err) + } + d.SetId(resp.GetTokenId()) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + orgID := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, orgID) + if err != nil { + return diag.FromErr(err) + } + + userID := d.Get(UserIDVar).(string) + resp, err := client.GetPersonalAccessTokenByIDs(ctx, &management.GetPersonalAccessTokenByIDsRequest{ + UserId: userID, + TokenId: d.Id(), + }) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get pat") + } + + set := map[string]interface{}{ + ExpirationDateVar: resp.GetToken().GetExpirationDate().AsTime().Format(time.RFC3339), + UserIDVar: userID, + helper.OrgIDVar: orgID, + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of pat: %v", k, err) + } + } + d.SetId(resp.GetToken().GetId()) + return nil +} diff --git a/zitadel/pat/resource.go b/zitadel/pat/resource.go new file mode 100644 index 00000000..b5ac883d --- /dev/null +++ b/zitadel/pat/resource.go @@ -0,0 +1,42 @@ +package pat + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing a personal access token of a user", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + UserIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the user", + ForceNew: true, + }, + TokenVar: { + Type: schema.TypeString, + Computed: true, + Description: "Value of the token", + Sensitive: true, + }, + ExpirationDateVar: { + Type: schema.TypeString, + Optional: true, + Description: "Expiration date of the token in the RFC3339 format", + ForceNew: true, + }, + }, + DeleteContext: delete, + CreateContext: create, + ReadContext: read, + Importer: helper.ImportWithIDAndOptionalOrg( + tokenIDVar, + helper.NewImportAttribute(UserIDVar, helper.ConvertID, false), + helper.NewImportAttribute(TokenVar, helper.ConvertNonEmpty, true), + ), + } +} diff --git a/zitadel/pat/resource_test.go b/zitadel/pat/resource_test.go new file mode 100644 index 00000000..dd24a877 --- /dev/null +++ b/zitadel/pat/resource_test.go @@ -0,0 +1,60 @@ +package pat_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/machine_user/machine_user_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/pat" +) + +func TestAccPersonalAccessToken(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_personal_access_token") + userDep, userID := machine_user_test_dep.Create(t, frame) + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, pat.ExpirationDateVar, exampleAttributes).AsString() + updatedProperty := "2051-01-01T00:00:00Z" + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, userDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + false, + checkRemoteProperty(*frame, userID), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(*frame, userID), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, pat.UserIDVar), + test_utils.ImportOrgId(frame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, pat.TokenVar), + ), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame, userID string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetPersonalAccessTokenByIDs(frame, &management.GetPersonalAccessTokenByIDsRequest{ + UserId: userID, + TokenId: frame.State(state).ID, + }) + if err != nil { + return err + } + actual := resp.GetToken().GetExpirationDate().AsTime().Format("2006-01-02T15:04:05Z") + if expect != actual { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/privacy_policy/const.go b/zitadel/privacy_policy/const.go new file mode 100644 index 00000000..524c1e02 --- /dev/null +++ b/zitadel/privacy_policy/const.go @@ -0,0 +1,8 @@ +package privacy_policy + +const ( + tosLinkVar = "tos_link" + privacyLinkVar = "privacy_link" + HelpLinkVar = "help_link" + supportEmailVar = "support_email" +) diff --git a/zitadel/privacy_policy/funcs.go b/zitadel/privacy_policy/funcs.go new file mode 100644 index 00000000..e240ade0 --- /dev/null +++ b/zitadel/privacy_policy/funcs.go @@ -0,0 +1,131 @@ +package privacy_policy + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.ResetPrivacyPolicyToDefault(ctx, &management.ResetPrivacyPolicyToDefaultRequest{}) + if err != nil { + return diag.Errorf("failed to reset privacy policy: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateCustomPrivacyPolicy(ctx, &management.UpdateCustomPrivacyPolicyRequest{ + TosLink: d.Get(tosLinkVar).(string), + PrivacyLink: d.Get(privacyLinkVar).(string), + HelpLink: d.Get(HelpLinkVar).(string), + SupportEmail: d.Get(supportEmailVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update privacy policy: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.AddCustomPrivacyPolicy(ctx, &management.AddCustomPrivacyPolicyRequest{ + TosLink: d.Get(tosLinkVar).(string), + PrivacyLink: d.Get(privacyLinkVar).(string), + HelpLink: d.Get(HelpLinkVar).(string), + SupportEmail: d.Get(supportEmailVar).(string), + }) + if err != nil { + return diag.Errorf("failed to create privacy policy: %v", err) + } + d.SetId(org) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetPrivacyPolicy(ctx, &management.GetPrivacyPolicyRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get privacy policy") + } + + policy := resp.Policy + if policy.GetIsDefault() == true { + d.SetId("") + return nil + } + set := map[string]interface{}{ + helper.OrgIDVar: policy.GetDetails().GetResourceOwner(), + tosLinkVar: policy.GetTosLink(), + privacyLinkVar: policy.GetPrivacyLink(), + HelpLinkVar: policy.GetHelpLink(), + supportEmailVar: policy.GetSupportEmail(), + } + + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of privacy policy: %v", k, err) + } + } + d.SetId(policy.GetDetails().GetResourceOwner()) + return nil +} diff --git a/zitadel/privacy_policy/resource.go b/zitadel/privacy_policy/resource.go new file mode 100644 index 00000000..6b2d8a7d --- /dev/null +++ b/zitadel/privacy_policy/resource.go @@ -0,0 +1,41 @@ +package privacy_policy + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the custom privacy policy of an organization.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + tosLinkVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + privacyLinkVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + HelpLinkVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + supportEmailVar: { + Type: schema.TypeString, + Required: true, + Description: "", + }, + }, + CreateContext: create, + DeleteContext: delete, + ReadContext: read, + UpdateContext: update, + Importer: helper.ImportWithOptionalOrg(), + } +} diff --git a/zitadel/privacy_policy/resource_test.go b/zitadel/privacy_policy/resource_test.go new file mode 100644 index 00000000..3748b408 --- /dev/null +++ b/zitadel/privacy_policy/resource_test.go @@ -0,0 +1,49 @@ +package privacy_policy_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/privacy_policy" +) + +func TestAccPrivacyPolicy(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_privacy_policy") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, privacy_policy.HelpLinkVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "http://example.com/acctest", + "", "", "", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + checkRemoteProperty(*frame)(""), + test_utils.ImportOrgId(frame), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetPrivacyPolicy(frame, &management.GetPrivacyPolicyRequest{}) + if err != nil { + return fmt.Errorf("getting policy failed: %w", err) + } + actual := resp.GetPolicy().GetHelpLink() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/project/const.go b/zitadel/project/const.go new file mode 100644 index 00000000..403e0ea9 --- /dev/null +++ b/zitadel/project/const.go @@ -0,0 +1,17 @@ +package project + +import "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/project" + +const ( + ProjectIDVar = "project_id" + NameVar = "name" + stateVar = "state" + roleAssertionVar = "project_role_assertion" + roleCheckVar = "project_role_check" + hasProjectCheckVar = "has_project_check" + privateLabelingSettingVar = "private_labeling_setting" +) + +var ( + defaultPrivateLabelingSetting = project.PrivateLabelingSetting_name[0] +) diff --git a/zitadel/project/datasource.go b/zitadel/project/datasource.go new file mode 100644 index 00000000..1ffa655b --- /dev/null +++ b/zitadel/project/datasource.go @@ -0,0 +1,52 @@ +package project + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing the project, which can then be granted to different organizations or users directly, containing different applications.", + Schema: map[string]*schema.Schema{ + ProjectIDVar: { + Type: schema.TypeString, + Required: true, + Description: "The ID of this resource.", + }, + NameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Name of the project", + }, + helper.OrgIDVar: helper.OrgIDResourceField, + stateVar: { + Type: schema.TypeString, + Computed: true, + Description: "State of the project", + }, + roleAssertionVar: { + Type: schema.TypeBool, + Computed: true, + Description: "describes if roles of user should be added in token", + }, + roleCheckVar: { + Type: schema.TypeBool, + Computed: true, + Description: "ZITADEL checks if the user has at least one on this project", + }, + hasProjectCheckVar: { + Type: schema.TypeBool, + Computed: true, + Description: "ZITADEL checks if the org of the user has permission to this project", + }, + privateLabelingSettingVar: { + Type: schema.TypeString, + Computed: true, + Description: "Defines from where the private labeling should be triggered", + }, + }, + ReadContext: read, + } +} diff --git a/zitadel/project/funcs.go b/zitadel/project/funcs.go new file mode 100644 index 00000000..7d7a763e --- /dev/null +++ b/zitadel/project/funcs.go @@ -0,0 +1,133 @@ +package project + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/project" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveProject(ctx, &management.RemoveProjectRequest{ + Id: d.Id(), + }) + if err != nil { + return diag.Errorf("failed to delete project: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateProject(ctx, &management.UpdateProjectRequest{ + Id: d.Id(), + Name: d.Get(NameVar).(string), + ProjectRoleCheck: d.Get(roleCheckVar).(bool), + ProjectRoleAssertion: d.Get(roleAssertionVar).(bool), + HasProjectCheck: d.Get(hasProjectCheckVar).(bool), + PrivateLabelingSetting: project.PrivateLabelingSetting(project.PrivateLabelingSetting_value[d.Get(privateLabelingSettingVar).(string)]), + }) + if err != nil { + return diag.Errorf("failed to update project: %v", err) + } + + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + plSetting := d.Get(privateLabelingSettingVar).(string) + resp, err := client.AddProject(ctx, &management.AddProjectRequest{ + Name: d.Get(NameVar).(string), + ProjectRoleAssertion: d.Get(roleAssertionVar).(bool), + ProjectRoleCheck: d.Get(roleCheckVar).(bool), + HasProjectCheck: d.Get(hasProjectCheckVar).(bool), + PrivateLabelingSetting: project.PrivateLabelingSetting(project.PrivateLabelingSetting_value[plSetting]), + }) + if err != nil { + return diag.Errorf("failed to create project: %v", err) + } + d.SetId(resp.GetId()) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetProjectByID(ctx, &management.GetProjectByIDRequest{Id: helper.GetID(d, ProjectIDVar)}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get project") + } + + project := resp.GetProject() + set := map[string]interface{}{ + helper.OrgIDVar: project.GetDetails().GetResourceOwner(), + stateVar: project.GetState().String(), + NameVar: project.GetName(), + roleAssertionVar: project.GetProjectRoleAssertion(), + roleCheckVar: project.GetProjectRoleCheck(), + hasProjectCheckVar: project.GetHasProjectCheck(), + privateLabelingSettingVar: project.PrivateLabelingSetting.String(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of project: %v", k, err) + } + } + d.SetId(project.GetId()) + + return nil +} diff --git a/zitadel/project/project_test_dep/dependency.go b/zitadel/project/project_test_dep/dependency.go new file mode 100644 index 00000000..d255f873 --- /dev/null +++ b/zitadel/project/project_test_dep/dependency.go @@ -0,0 +1,17 @@ +package project_test_dep + +import ( + "testing" + + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project" +) + +func Create(t *testing.T, frame *test_utils.OrgTestFrame) (string, string) { + return test_utils.CreateDefaultDependency(t, "zitadel_project", project.ProjectIDVar, func() (string, error) { + p, err := frame.AddProject(frame, &management.AddProjectRequest{Name: frame.UniqueResourcesID}) + return p.GetId(), err + }) +} diff --git a/zitadel/project/resource.go b/zitadel/project/resource.go new file mode 100644 index 00000000..a8ef35c3 --- /dev/null +++ b/zitadel/project/resource.go @@ -0,0 +1,62 @@ +package project + +import ( + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/project" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the project, which can then be granted to different organizations or users directly, containing different applications.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + NameVar: { + Type: schema.TypeString, + Required: true, + Description: "Name of the project", + }, + stateVar: { + Type: schema.TypeString, + Computed: true, + Description: "State of the project", + /* Not necessary as long as only active projects are created + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return EnumValueValidation(projectStateVar, value, project.ProjectState_value) + },*/ + }, + roleAssertionVar: { + Type: schema.TypeBool, + Optional: true, + Description: "describes if roles of user should be added in token", + }, + roleCheckVar: { + Type: schema.TypeBool, + Optional: true, + Description: "ZITADEL checks if the user has at least one on this project", + }, + hasProjectCheckVar: { + Type: schema.TypeBool, + Optional: true, + Description: "ZITADEL checks if the org of the user has permission to this project", + }, + privateLabelingSettingVar: { + Type: schema.TypeString, + Optional: true, + Description: "Defines from where the private labeling should be triggered" + helper.DescriptionEnumValuesList(project.PrivateLabelingSetting_name), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(privateLabelingSettingVar, value, project.PrivateLabelingSetting_value) + }, + Default: defaultPrivateLabelingSetting, + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithIDAndOptionalOrg(ProjectIDVar), + } +} diff --git a/zitadel/project/resource_test.go b/zitadel/project/resource_test.go new file mode 100644 index 00000000..47b13cb1 --- /dev/null +++ b/zitadel/project/resource_test.go @@ -0,0 +1,53 @@ +package project_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project" +) + +func TestAccProject(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_project") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, project.NameVar, exampleAttributes).AsString() + updatedProperty := "updatedproperty" + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + false, + checkRemoteProperty(frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(frame), updatedProperty), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetProjectByID(frame, &management.GetProjectByIDRequest{Id: frame.State(state).ID}) + if err != nil { + return err + } + actual := remoteResource.GetProject().GetName() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/project_grant/const.go b/zitadel/project_grant/const.go new file mode 100644 index 00000000..48a55dc4 --- /dev/null +++ b/zitadel/project_grant/const.go @@ -0,0 +1,7 @@ +package project_grant + +const ( + ProjectIDVar = "project_id" + grantedOrgIDVar = "granted_org_id" + RoleKeysVar = "role_keys" +) diff --git a/zitadel/project_grant/funcs.go b/zitadel/project_grant/funcs.go new file mode 100644 index 00000000..97f1d2f0 --- /dev/null +++ b/zitadel/project_grant/funcs.go @@ -0,0 +1,122 @@ +package project_grant + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveProjectGrant(ctx, &management.RemoveProjectGrantRequest{ + GrantId: d.Id(), + ProjectId: d.Get(ProjectIDVar).(string), + }) + if err != nil { + return diag.Errorf("failed to delete projectgrant: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateProjectGrant(ctx, &management.UpdateProjectGrantRequest{ + GrantId: d.Id(), + ProjectId: d.Get(ProjectIDVar).(string), + RoleKeys: helper.GetOkSetToStringSlice(d, RoleKeysVar), + }) + if err != nil { + return diag.Errorf("failed to update projectgrant: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.AddProjectGrant(ctx, &management.AddProjectGrantRequest{ + GrantedOrgId: d.Get(grantedOrgIDVar).(string), + ProjectId: d.Get(ProjectIDVar).(string), + RoleKeys: helper.GetOkSetToStringSlice(d, RoleKeysVar), + }) + if err != nil { + return diag.Errorf("failed to create projectgrant: %v", err) + } + d.SetId(resp.GetGrantId()) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetProjectGrantByID(ctx, &management.GetProjectGrantByIDRequest{ProjectId: d.Get(ProjectIDVar).(string), GrantId: d.Id()}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get projectgrant") + } + + projectGrant := resp.GetProjectGrant() + set := map[string]interface{}{ + ProjectIDVar: projectGrant.GetProjectId(), + grantedOrgIDVar: projectGrant.GetGrantedOrgId(), + RoleKeysVar: projectGrant.GetGrantedRoleKeys(), + helper.OrgIDVar: projectGrant.GetDetails().GetResourceOwner(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of projectgrant: %v", k, err) + } + } + d.SetId(projectGrant.GetGrantId()) + return nil +} diff --git a/zitadel/project_grant/project_grant_test_dep/dependency.go b/zitadel/project_grant/project_grant_test_dep/dependency.go new file mode 100644 index 00000000..5b5a0239 --- /dev/null +++ b/zitadel/project_grant/project_grant_test_dep/dependency.go @@ -0,0 +1,20 @@ +package project_grant_test_dep + +import ( + "testing" + + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" +) + +func Create(t *testing.T, frame *test_utils.OrgTestFrame, projectID, grantedOrgID string) string { + dep, err := frame.AddProjectGrant(frame, &management.AddProjectGrantRequest{ + ProjectId: projectID, + GrantedOrgId: grantedOrgID, + }) + if err != nil { + t.Errorf("failed to create a project grant: %v", err) + } + return dep.GetGrantId() +} diff --git a/zitadel/project_grant/resource.go b/zitadel/project_grant/resource.go new file mode 100644 index 00000000..3147177d --- /dev/null +++ b/zitadel/project_grant/resource.go @@ -0,0 +1,44 @@ +package project_grant + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the grant of a project to a different organization, also containing the available roles which can be given to the members of the projectgrant.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + ProjectIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the project", + ForceNew: true, + }, + grantedOrgIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the organization granted the project", + ForceNew: true, + }, + RoleKeysVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "List of roles granted", + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithIDAndOptionalOrg( + "", + helper.NewImportAttribute(ProjectIDVar, helper.ConvertID, false), + ), + } +} diff --git a/zitadel/project_grant/resource_test.go b/zitadel/project_grant/resource_test.go new file mode 100644 index 00000000..21401235 --- /dev/null +++ b/zitadel/project_grant/resource_test.go @@ -0,0 +1,66 @@ +package project_grant_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org/org_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project/project_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_grant" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_role/project_role_test_dep" +) + +func TestAccProjectGrant(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_project_grant") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, project_grant.RoleKeysVar, exampleAttributes).AsValueSlice()[0].AsString() + updatedProperty := "updatedproperty" + projectDep, projectID := project_test_dep.Create(t, frame) + project_role_test_dep.Create(t, frame, projectID, exampleProperty, updatedProperty) + grantedOrgDep, _, _ := org_test_dep.Create(t, frame, "granted_org") + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, projectDep, grantedOrgDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + false, + checkRemoteProperty(*frame, projectID), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(*frame, projectID), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, project_grant.ProjectIDVar), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame, projectID string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetProjectGrantByID(frame, &management.GetProjectGrantByIDRequest{ + ProjectId: projectID, + GrantId: frame.State(state).ID, + }) + if err != nil { + return err + } + actualRoleKeys := resp.GetProjectGrant().GetGrantedRoleKeys() + if len(actualRoleKeys) != 1 { + return fmt.Errorf("expected 1 role, but got %d", len(actualRoleKeys)) + } + if expect != actualRoleKeys[0] { + return fmt.Errorf("expected role key %s, but got %s", expect, actualRoleKeys[0]) + } + return nil + } + } +} diff --git a/zitadel/project_grant_member/const.go b/zitadel/project_grant_member/const.go new file mode 100644 index 00000000..ba48b0bb --- /dev/null +++ b/zitadel/project_grant_member/const.go @@ -0,0 +1,8 @@ +package project_grant_member + +const ( + ProjectIDVar = "project_id" + GrantIDVar = "grant_id" + UserIDVar = "user_id" + RolesVar = "roles" +) diff --git a/zitadel/project_grant_member/funcs.go b/zitadel/project_grant_member/funcs.go new file mode 100644 index 00000000..08f0aa3f --- /dev/null +++ b/zitadel/project_grant_member/funcs.go @@ -0,0 +1,153 @@ +package project_grant_member + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/member" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveProjectGrantMember(ctx, &management.RemoveProjectGrantMemberRequest{ + UserId: d.Get(UserIDVar).(string), + ProjectId: d.Get(ProjectIDVar).(string), + GrantId: d.Get(GrantIDVar).(string), + }) + if err != nil { + return diag.Errorf("failed to delete projectmember: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateProjectGrantMember(ctx, &management.UpdateProjectGrantMemberRequest{ + UserId: d.Get(UserIDVar).(string), + Roles: helper.GetOkSetToStringSlice(d, RolesVar), + ProjectId: d.Get(ProjectIDVar).(string), + GrantId: d.Get(GrantIDVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update projectmember: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + userID := d.Get(UserIDVar).(string) + projectID := d.Get(ProjectIDVar).(string) + grantID := d.Get(GrantIDVar).(string) + _, err = client.AddProjectGrantMember(ctx, &management.AddProjectGrantMemberRequest{ + UserId: userID, + ProjectId: projectID, + GrantId: grantID, + Roles: helper.GetOkSetToStringSlice(d, RolesVar), + }) + if err != nil { + return diag.Errorf("failed to create projectgrantmember: %v", err) + } + d.SetId(getProjectGrantMemberID(org, projectID, grantID, userID)) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + projectID := d.Get(ProjectIDVar).(string) + grantID := d.Get(GrantIDVar).(string) + userID := d.Get(UserIDVar).(string) + resp, err := client.ListProjectGrantMembers(ctx, &management.ListProjectGrantMembersRequest{ + ProjectId: projectID, + GrantId: grantID, + Queries: []*member.SearchQuery{{ + Query: &member.SearchQuery_UserIdQuery{ + UserIdQuery: &member.UserIDQuery{ + UserId: userID, + }, + }, + }}, + }) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to list projectgrantmembers") + } + + if len(resp.Result) == 1 { + memberRes := resp.Result[0] + set := map[string]interface{}{ + UserIDVar: userID, + helper.OrgIDVar: org, + ProjectIDVar: projectID, + RolesVar: memberRes.GetRoles(), + GrantIDVar: grantID, + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of projectgrantmember: %v", k, err) + } + } + d.SetId(getProjectGrantMemberID(org, projectID, grantID, userID)) + return nil + } + + d.SetId("") + return nil +} + +func getProjectGrantMemberID(org, projectID, grantID, userID string) string { + return org + "_" + projectID + "_" + grantID + "_" + userID +} diff --git a/zitadel/project_grant_member/resource.go b/zitadel/project_grant_member/resource.go new file mode 100644 index 00000000..31e69e90 --- /dev/null +++ b/zitadel/project_grant_member/resource.go @@ -0,0 +1,52 @@ +package project_grant_member + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the membership of a user on an granted project, defined with the given role.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + ProjectIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the project", + ForceNew: true, + }, + GrantIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the grant", + ForceNew: true, + }, + UserIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the user", + ForceNew: true, + }, + RolesVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + Description: "List of roles granted", + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithEmptyID( + helper.ImportOptionalOrgAttribute, + helper.NewImportAttribute(ProjectIDVar, helper.ConvertID, false), + helper.NewImportAttribute(GrantIDVar, helper.ConvertID, false), + helper.NewImportAttribute(UserIDVar, helper.ConvertID, false), + ), + } +} diff --git a/zitadel/project_grant_member/resource_test.go b/zitadel/project_grant_member/resource_test.go new file mode 100644 index 00000000..2757b035 --- /dev/null +++ b/zitadel/project_grant_member/resource_test.go @@ -0,0 +1,89 @@ +package project_grant_member_test + +import ( + "fmt" + "regexp" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/member" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/human_user/human_user_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org/org_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project/project_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_grant/project_grant_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_grant_member" +) + +func TestAccProjectGrantMember(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_project_grant_member") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, project_grant_member.RolesVar, exampleAttributes).AsValueSlice()[0].AsString() + grantIDProperty := test_utils.AttributeValue(t, project_grant_member.GrantIDVar, exampleAttributes).AsString() + projectDep, projectID := project_test_dep.Create(t, frame) + userDep, userID := human_user_test_dep.Create(t, frame) + _, grantedOrgID, _ := org_test_dep.Create(t, frame, "granting_org") + grantID := project_grant_test_dep.Create(t, frame, projectID, grantedOrgID) + resourceExample = strings.Replace(resourceExample, grantIDProperty, grantID, 1) + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, projectDep, userDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "PROJECT_GRANT_OWNER_VIEWER", + "", "", "", + true, + checkRemoteProperty(*frame, projectID, grantID, userID), + regexp.MustCompile(fmt.Sprintf( + "^%s_%s_%s_%s$", + helper.ZitadelGeneratedIdPattern, + helper.ZitadelGeneratedIdPattern, + helper.ZitadelGeneratedIdPattern, + helper.ZitadelGeneratedIdPattern, + )), + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(*frame, projectID, grantID, userID), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportStateAttribute(frame.BaseTestFrame, project_grant_member.ProjectIDVar), + test_utils.ImportStateAttribute(frame.BaseTestFrame, project_grant_member.GrantIDVar), + test_utils.ImportStateAttribute(frame.BaseTestFrame, project_grant_member.UserIDVar), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame, projectID, grantID, userID string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.ListProjectGrantMembers(frame, &management.ListProjectGrantMembersRequest{ + ProjectId: projectID, + GrantId: grantID, + Queries: []*member.SearchQuery{{ + Query: &member.SearchQuery_UserIdQuery{ + UserIdQuery: &member.UserIDQuery{ + UserId: userID, + }, + }, + }}, + }) + if err != nil { + return err + } + if len(resp.Result) != 1 { + return fmt.Errorf("expected 1 result, but got %d: %w", len(resp.Result), test_utils.ErrNotFound) + } + actualRoleKeys := resp.Result[0].GetRoles() + if len(actualRoleKeys) != 1 { + return fmt.Errorf("expected 1 role, but got %d", len(actualRoleKeys)) + } + if expect != actualRoleKeys[0] { + return fmt.Errorf("expected role key %s, but got %s", expect, actualRoleKeys[0]) + } + return nil + } + } +} diff --git a/zitadel/project_member/const.go b/zitadel/project_member/const.go new file mode 100644 index 00000000..a2fb16c0 --- /dev/null +++ b/zitadel/project_member/const.go @@ -0,0 +1,7 @@ +package project_member + +const ( + ProjectIDVar = "project_id" + UserIDVar = "user_id" + rolesVar = "roles" +) diff --git a/zitadel/project_member/funcs.go b/zitadel/project_member/funcs.go new file mode 100644 index 00000000..ada573bd --- /dev/null +++ b/zitadel/project_member/funcs.go @@ -0,0 +1,146 @@ +package project_member + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/member" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveProjectMember(ctx, &management.RemoveProjectMemberRequest{ + UserId: d.Get(UserIDVar).(string), + ProjectId: d.Get(ProjectIDVar).(string), + }) + if err != nil { + return diag.Errorf("failed to delete projectmember: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateProjectMember(ctx, &management.UpdateProjectMemberRequest{ + UserId: d.Get(UserIDVar).(string), + Roles: helper.GetOkSetToStringSlice(d, rolesVar), + ProjectId: d.Get(ProjectIDVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update projectmember: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + userID := d.Get(UserIDVar).(string) + projectID := d.Get(ProjectIDVar).(string) + _, err = client.AddProjectMember(ctx, &management.AddProjectMemberRequest{ + UserId: userID, + ProjectId: projectID, + Roles: helper.GetOkSetToStringSlice(d, rolesVar), + }) + if err != nil { + return diag.Errorf("failed to create projectmember: %v", err) + } + d.SetId(getProjectMemberID(org, projectID, userID)) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + org := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, org) + if err != nil { + return diag.FromErr(err) + } + + projectID := d.Get(ProjectIDVar).(string) + userID := d.Get(UserIDVar).(string) + resp, err := client.ListProjectMembers(ctx, &management.ListProjectMembersRequest{ + ProjectId: projectID, + Queries: []*member.SearchQuery{{ + Query: &member.SearchQuery_UserIdQuery{ + UserIdQuery: &member.UserIDQuery{ + UserId: userID, + }, + }, + }}, + }) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to list projectmembers") + } + + if len(resp.Result) == 1 { + memberRes := resp.Result[0] + set := map[string]interface{}{ + UserIDVar: memberRes.GetUserId(), + helper.OrgIDVar: memberRes.GetDetails().GetResourceOwner(), + ProjectIDVar: projectID, + rolesVar: memberRes.GetRoles(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of projectmember: %v", k, err) + } + } + d.SetId(getProjectMemberID(org, projectID, userID)) + return nil + } + + d.SetId("") + return nil +} + +func getProjectMemberID(org string, projectID string, userID string) string { + return org + "_" + projectID + "_" + userID +} diff --git a/zitadel/project_member/resource.go b/zitadel/project_member/resource.go new file mode 100644 index 00000000..e7c11134 --- /dev/null +++ b/zitadel/project_member/resource.go @@ -0,0 +1,45 @@ +package project_member + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the membership of a user on an project, defined with the given role.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + ProjectIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the project", + ForceNew: true, + }, + UserIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the user", + ForceNew: true, + }, + rolesVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + Description: "List of roles granted", + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithEmptyID( + helper.NewImportAttribute(ProjectIDVar, helper.ConvertID, false), + helper.NewImportAttribute(UserIDVar, helper.ConvertID, false), + helper.ImportOptionalOrgAttribute, + ), + } +} diff --git a/zitadel/project_member/resource_test.go b/zitadel/project_member/resource_test.go new file mode 100644 index 00000000..2aaa04fc --- /dev/null +++ b/zitadel/project_member/resource_test.go @@ -0,0 +1,68 @@ +package project_member_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/member" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/human_user/human_user_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project/project_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_grant_member" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_member" +) + +func TestAccProjectMember(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_project_member") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, project_grant_member.RolesVar, exampleAttributes).AsValueSlice()[0].AsString() + projectDep, projectID := project_test_dep.Create(t, frame) + userDep, userID := human_user_test_dep.Create(t, frame) + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, projectDep, userDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "PROJECT_OWNER_VIEWER", + "", "", "", + true, + checkRemoteProperty(*frame, projectID, userID), + regexp.MustCompile(fmt.Sprintf("^%s_%s_%s$", helper.ZitadelGeneratedIdPattern, helper.ZitadelGeneratedIdPattern, helper.ZitadelGeneratedIdPattern)), + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(*frame, projectID, userID), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportStateAttribute(frame.BaseTestFrame, project_member.ProjectIDVar), + test_utils.ImportStateAttribute(frame.BaseTestFrame, project_member.UserIDVar), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame, projectID, userID string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.ListProjectMembers(frame, &management.ListProjectMembersRequest{ + ProjectId: projectID, + Queries: []*member.SearchQuery{{ + Query: &member.SearchQuery_UserIdQuery{UserIdQuery: &member.UserIDQuery{UserId: userID}}, + }}, + }) + if err != nil { + return err + } + if len(resp.Result) == 0 || len(resp.Result[0].Roles) == 0 { + return fmt.Errorf("expected 1 user with 1 role, but got %d: %w", len(resp.Result), test_utils.ErrNotFound) + } + actual := resp.Result[0].Roles[0] + if expect != actual { + return fmt.Errorf("expected role %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/project_role/const.go b/zitadel/project_role/const.go new file mode 100644 index 00000000..d121383b --- /dev/null +++ b/zitadel/project_role/const.go @@ -0,0 +1,8 @@ +package project_role + +const ( + ProjectIDVar = "project_id" + KeyVar = "role_key" + displayNameVar = "display_name" + groupVar = "group" +) diff --git a/zitadel/project_role/datasource.go b/zitadel/project_role/datasource.go new file mode 100644 index 00000000..5ee1a13e --- /dev/null +++ b/zitadel/project_role/datasource.go @@ -0,0 +1,41 @@ +package project_role + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Datasource representing the project roles, which can be given as authorizations to users.", + Schema: map[string]*schema.Schema{ + ProjectIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the project", + }, + helper.OrgIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the organization", + }, + KeyVar: { + Type: schema.TypeString, + Required: true, + Description: "Key used for project role", + }, + displayNameVar: { + Type: schema.TypeString, + Computed: true, + Description: "Name used for project role", + }, + groupVar: { + Type: schema.TypeString, + Computed: true, + Description: "Group used for project role", + }, + }, + ReadContext: read, + } +} diff --git a/zitadel/project_role/funcs.go b/zitadel/project_role/funcs.go new file mode 100644 index 00000000..e0991025 --- /dev/null +++ b/zitadel/project_role/funcs.go @@ -0,0 +1,151 @@ +package project_role + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/object" + project2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/project" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveProjectRole(ctx, &management.RemoveProjectRoleRequest{ + ProjectId: d.Get(ProjectIDVar).(string), + RoleKey: d.Get(KeyVar).(string), + }) + if err != nil { + return diag.Errorf("failed to delete project role: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateProjectRole(ctx, &management.UpdateProjectRoleRequest{ + ProjectId: d.Get(ProjectIDVar).(string), + RoleKey: d.Get(KeyVar).(string), + DisplayName: d.Get(displayNameVar).(string), + Group: d.Get(groupVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update project role: %v", err) + } + + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + orgID := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, orgID) + if err != nil { + return diag.FromErr(err) + } + + projectID := d.Get(ProjectIDVar).(string) + roleKey := d.Get(KeyVar).(string) + _, err = client.AddProjectRole(ctx, &management.AddProjectRoleRequest{ + ProjectId: projectID, + RoleKey: roleKey, + DisplayName: d.Get(displayNameVar).(string), + Group: d.Get(groupVar).(string), + }) + if err != nil { + return diag.Errorf("failed to create project role: %v", err) + } + d.SetId(getProjectRoleID(orgID, projectID, roleKey)) + + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + orgID := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, orgID) + if err != nil { + return diag.FromErr(err) + } + + projectID := d.Get(ProjectIDVar).(string) + resp, err := client.ListProjectRoles(ctx, &management.ListProjectRolesRequest{ + ProjectId: projectID, + Queries: []*project2.RoleQuery{ + {Query: &project2.RoleQuery_KeyQuery{ + KeyQuery: &project2.RoleKeyQuery{ + Key: d.Get(KeyVar).(string), + Method: object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS, + }, + }}, + }, + }) + if err != nil || resp.Result == nil || len(resp.Result) == 0 { + d.SetId("") + return nil + } + + if len(resp.Result) == 1 { + projectRole := resp.GetResult()[0] + roleKey := projectRole.GetKey() + set := map[string]interface{}{ + ProjectIDVar: projectID, + helper.OrgIDVar: orgID, + KeyVar: roleKey, + displayNameVar: projectRole.GetDisplayName(), + groupVar: projectRole.GetGroup(), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of project: %v", k, err) + } + } + d.SetId(getProjectRoleID(orgID, projectID, roleKey)) + return nil + } + + d.SetId("") + return nil +} + +func getProjectRoleID(orgID string, projectID string, roleKey string) string { + return orgID + "_" + projectID + "_" + roleKey +} diff --git a/zitadel/project_role/project_role_test_dep/dependency.go b/zitadel/project_role/project_role_test_dep/dependency.go new file mode 100644 index 00000000..de359bc2 --- /dev/null +++ b/zitadel/project_role/project_role_test_dep/dependency.go @@ -0,0 +1,27 @@ +package project_role_test_dep + +import ( + "strings" + "testing" + + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_role" +) + +func Create(t *testing.T, frame *test_utils.OrgTestFrame, projectID string, key ...string) string { + deps := make([]string, len(key)) + for i, k := range key { + _, dep := test_utils.CreateDefaultDependency(t, "zitadel_project_role", project_role.KeyVar, func() (string, error) { + _, err := frame.AddProjectRole(frame, &management.AddProjectRoleRequest{ + ProjectId: projectID, + RoleKey: k, + DisplayName: k, + }) + return k, err + }) + deps[i] = dep + } + return strings.Join(deps, "\n") +} diff --git a/zitadel/project_role/resource.go b/zitadel/project_role/resource.go new file mode 100644 index 00000000..dd2848fd --- /dev/null +++ b/zitadel/project_role/resource.go @@ -0,0 +1,47 @@ +package project_role + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the project roles, which can be given as authorizations to users.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + ProjectIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the project", + ForceNew: true, + }, + KeyVar: { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Key used for project role", + }, + displayNameVar: { + Type: schema.TypeString, + Required: true, + Description: "Name used for project role", + }, + groupVar: { + Type: schema.TypeString, + Optional: true, + Description: "Group used for project role", + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithEmptyID( + helper.NewImportAttribute(ProjectIDVar, helper.ConvertID, false), + helper.NewImportAttribute(KeyVar, helper.ConvertNonEmpty, false), + helper.ImportOptionalOrgAttribute, + ), + } +} diff --git a/zitadel/project_role/resource_test.go b/zitadel/project_role/resource_test.go new file mode 100644 index 00000000..38366a20 --- /dev/null +++ b/zitadel/project_role/resource_test.go @@ -0,0 +1,72 @@ +package project_role_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/project" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project/project_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_role" +) + +func TestAccProjectRole(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_project_role") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, project_role.KeyVar, exampleAttributes).AsString() + updatedProperty := "updatedProperty" + projectDep, projectID := project_test_dep.Create(t, frame) + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, projectDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + true, + checkRemoteProperty(*frame, projectID), + regexp.MustCompile(fmt.Sprintf("^%s_%s_(%s|%s)$", helper.ZitadelGeneratedIdPattern, helper.ZitadelGeneratedIdPattern, exampleProperty, updatedProperty)), + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(*frame, projectID), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportStateAttribute(frame.BaseTestFrame, project_role.ProjectIDVar), + test_utils.ImportStateAttribute(frame.BaseTestFrame, project_role.KeyVar), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame, projectID string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.ListProjectRoles(frame, &management.ListProjectRolesRequest{ + ProjectId: projectID, + Queries: []*project.RoleQuery{{ + Query: &project.RoleQuery_KeyQuery{ + KeyQuery: &project.RoleKeyQuery{Key: expect}, + }, + }}, + }) + if err != nil { + return err + } + actualRoles := resp.GetResult() + if len(actualRoles) == 0 { + return test_utils.ErrNotFound + } + if len(actualRoles) != 1 { + return fmt.Errorf("expected 1 role, but got %v", actualRoles) + } + actualRole := actualRoles[0].GetKey() + if actualRole != expect { + return fmt.Errorf("expected role key %s, but got %s", expect, actualRole) + } + return nil + } + } +} diff --git a/zitadel/provider.go b/zitadel/provider.go index b14437ea..f58d25f8 100644 --- a/zitadel/provider.go +++ b/zitadel/provider.go @@ -2,68 +2,327 @@ package zitadel import ( "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + fdiag "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/provider" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - v2 "github.com/zitadel/terraform-provider-zitadel/zitadel/v2" + zitadel_go "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/action" + "github.com/zitadel/terraform-provider-zitadel/zitadel/application_api" + "github.com/zitadel/terraform-provider-zitadel/zitadel/application_key" + "github.com/zitadel/terraform-provider-zitadel/zitadel/application_oidc" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_domain_claimed_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_domain_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_init_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_label_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_lockout_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_login_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_login_texts" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_notification_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_oidc_settings" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_password_change_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_password_complexity_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_password_reset_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_passwordless_registration_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_privacy_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_verify_email_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/default_verify_phone_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/domain" + "github.com/zitadel/terraform-provider-zitadel/zitadel/domain_claimed_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/domain_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/human_user" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_azure_ad" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_github" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_github_es" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_gitlab" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_gitlab_self_hosted" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_google" + "github.com/zitadel/terraform-provider-zitadel/zitadel/idp_ldap" + "github.com/zitadel/terraform-provider-zitadel/zitadel/init_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/instance_member" + "github.com/zitadel/terraform-provider-zitadel/zitadel/label_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/lockout_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/login_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/login_texts" + "github.com/zitadel/terraform-provider-zitadel/zitadel/machine_key" + "github.com/zitadel/terraform-provider-zitadel/zitadel/machine_user" + "github.com/zitadel/terraform-provider-zitadel/zitadel/notification_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_azure_ad" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_github" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_github_es" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_gitlab" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_gitlab_self_hosted" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_google" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_jwt" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_ldap" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_idp_oidc" + "github.com/zitadel/terraform-provider-zitadel/zitadel/org_member" + "github.com/zitadel/terraform-provider-zitadel/zitadel/password_change_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/password_complexity_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/password_reset_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/passwordless_registration_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/pat" + "github.com/zitadel/terraform-provider-zitadel/zitadel/privacy_policy" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_grant" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_grant_member" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_member" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_role" + "github.com/zitadel/terraform-provider-zitadel/zitadel/sms_provider_twilio" + "github.com/zitadel/terraform-provider-zitadel/zitadel/smtp_config" + "github.com/zitadel/terraform-provider-zitadel/zitadel/trigger_actions" + "github.com/zitadel/terraform-provider-zitadel/zitadel/user_grant" + "github.com/zitadel/terraform-provider-zitadel/zitadel/verify_email_message_text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/verify_phone_message_text" ) +var _ provider.Provider = (*providerPV6)(nil) + +type providerPV6 struct { + customOptions []zitadel_go.Option +} + +func NewProviderPV6(option ...zitadel_go.Option) provider.Provider { + return &providerPV6{customOptions: option} +} + +type providerModel struct { + Insecure types.Bool `tfsdk:"insecure"` + Domain types.String `tfsdk:"domain"` + Port types.String `tfsdk:"port"` + Token types.String `tfsdk:"token"` + JWTProfileFile types.String `tfsdk:"jwt_profile_file"` + JWTProfileJSON types.String `tfsdk:"jwt_profile_json"` +} + +func (p *providerPV6) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "zitadel" +} +func (p *providerPV6) GetSchema(_ context.Context) (tfsdk.Schema, fdiag.Diagnostics) { + return tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + helper.DomainVar: { + Type: types.StringType, + Required: true, + Description: "Domain used to connect to the ZITADEL instance", + }, + helper.InsecureVar: { + Type: types.BoolType, + Optional: true, + Description: "Use insecure connection", + }, + helper.TokenVar: { + Type: types.StringType, + Optional: true, + Description: "Path to the file containing credentials to connect to ZITADEL", + }, + helper.JWTProfileFile: { + Type: types.StringType, + Optional: true, + Description: "Path to the file containing credentials to connect to ZITADEL. Either 'jwt_profile_file' or 'jwt_profile_json' is required", + }, + helper.JWTProfileJSON: { + Type: types.StringType, + Optional: true, + Description: "JSON value of credentials to connect to ZITADEL. Either 'jwt_profile_file' or 'jwt_profile_json' is required", + }, + helper.PortVar: { + Type: types.StringType, + Optional: true, + Description: "Used port if not the default ports 80 or 443 are configured", + }, + }, + }, nil +} + +func (p *providerPV6) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + var config providerModel + diags := req.Config.Get(ctx, &config) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + info, err := helper.GetClientInfo( + config.Insecure.ValueBool(), + config.Domain.ValueString(), + config.Token.ValueString(), + config.JWTProfileFile.ValueString(), + config.JWTProfileJSON.ValueString(), + config.Port.ValueString(), + ) + if err != nil { + resp.Diagnostics.AddError("failed to handle provider config", err.Error()) + return + } + + resp.DataSourceData = info + resp.ResourceData = info +} + +func (p *providerPV6) DataSources(_ context.Context) []func() datasource.DataSource { + return nil +} + +func (p *providerPV6) Resources(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + init_message_text.New, + login_texts.New, + password_reset_message_text.New, + password_change_message_text.New, + verify_email_message_text.New, + verify_phone_message_text.New, + domain_claimed_message_text.New, + passwordless_registration_message_text.New, + default_domain_claimed_message_text.New, + default_init_message_text.New, + default_login_texts.New, + default_password_reset_message_text.New, + default_password_change_message_text.New, + default_passwordless_registration_message_text.New, + default_verify_email_message_text.New, + default_verify_phone_message_text.New, + } +} + func Provider() *schema.Provider { return &schema.Provider{ DataSourcesMap: map[string]*schema.Resource{ - // "zitadelV1Org": v1.GetOrgDatasource(), + "zitadel_org": org.GetDatasource(), + "zitadel_orgs": org.ListDatasources(), + "zitadel_human_user": human_user.GetDatasource(), + "zitadel_machine_user": machine_user.GetDatasource(), + "zitadel_project": project.GetDatasource(), + "zitadel_project_role": project_role.GetDatasource(), + "zitadel_action": action.GetDatasource(), + "zitadel_application_oidc": application_oidc.GetDatasource(), + "zitadel_application_api": application_api.GetDatasource(), + "zitadel_trigger_actions": trigger_actions.GetDatasource(), + "zitadel_idp_github": idp_github.GetDatasource(), + "zitadel_idp_github_es": idp_github_es.GetDatasource(), + "zitadel_idp_gitlab": idp_gitlab.GetDatasource(), + "zitadel_idp_gitlab_self_hosted": idp_gitlab_self_hosted.GetDatasource(), + "zitadel_idp_google": idp_google.GetDatasource(), + "zitadel_idp_azure_ad": idp_azure_ad.GetDatasource(), + "zitadel_idp_ldap": idp_ldap.GetDatasource(), + "zitadel_org_jwt_idp": org_idp_jwt.GetDatasource(), + "zitadel_org_oidc_idp": org_idp_oidc.GetDatasource(), + "zitadel_org_idp_github": org_idp_github.GetDatasource(), + "zitadel_org_idp_github_es": org_idp_github_es.GetDatasource(), + "zitadel_org_idp_gitlab": org_idp_gitlab.GetDatasource(), + "zitadel_org_idp_gitlab_self_hosted": org_idp_gitlab_self_hosted.GetDatasource(), + "zitadel_org_idp_google": org_idp_google.GetDatasource(), + "zitadel_org_idp_azure_ad": org_idp_azure_ad.GetDatasource(), + "zitadel_org_idp_ldap": org_idp_ldap.GetDatasource(), + "zitadel_default_oidc_settings": default_oidc_settings.GetDatasource(), }, Schema: map[string]*schema.Schema{ - v2.IssuerVar: { + helper.DomainVar: { Type: schema.TypeString, Required: true, - DefaultFunc: schema.EnvDefaultFunc("ISSUER", ""), + Description: "Domain used to connect to the ZITADEL instance", }, - v2.AddressVar: { + helper.InsecureVar: { + Type: schema.TypeBool, + Optional: true, + Description: "Use insecure connection", + }, + helper.TokenVar: { Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("ADDRESS", ""), + Optional: true, + Description: "Path to the file containing credentials to connect to ZITADEL", }, - v2.ProjectVar: { + helper.JWTProfileFile: { Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("PROJECT", ""), + Optional: true, + Description: "Path to the file containing credentials to connect to ZITADEL. Either 'jwt_profile_file' or 'jwt_profile_json' is required", }, - v2.TokenVar: { + helper.JWTProfileJSON: { Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("TOKEN", ""), + Optional: true, + Description: "JSON value of credentials to connect to ZITADEL. Either 'jwt_profile_file' or 'jwt_profile_json' is required", + }, + helper.PortVar: { + Type: schema.TypeString, + Optional: true, + Description: "Used port if not the default ports 80 or 443 are configured", }, }, ResourcesMap: map[string]*schema.Resource{ - "org": v2.OrgResource(), - "human_user": v2.GetHumanUser(), - "machine_user": v2.GetMachineUser(), - "project": v2.GetProject(), - "project_role": v2.GetProjectRole(), - "domain": v2.GetDomain(), - "action": v2.GetAction(), - "application_oidc": v2.GetApplicationOIDC(), - "application_api": v2.GetApplicationAPI(), - "project_grant": v2.GetProjectGrant(), - "user_grant": v2.GetUserGrant(), - "org_member": v2.GetOrgMember(), - "project_member": v2.GetProjectMember(), - "project_grant_member": v2.GetProjectGrantMember(), - /* - "domain_policy": v2.GetDomainPolicy(), - "label_policy": v2.GetLabelPolicy(), - "lockout_policy": v2.GetLockoutPolicy(), - "login_policy": v2.GetLoginPolicy(), - "password_complexity_policy": v2.GetPasswordComplexityPolicy(), - "privacy_policy": v2.GetPrivacyPolicy(), - */ + "zitadel_org": org.GetResource(), + "zitadel_human_user": human_user.GetResource(), + "zitadel_machine_user": machine_user.GetResource(), + "zitadel_project": project.GetResource(), + "zitadel_project_role": project_role.GetResource(), + "zitadel_domain": domain.GetResource(), + "zitadel_action": action.GetResource(), + "zitadel_application_oidc": application_oidc.GetResource(), + "zitadel_application_api": application_api.GetResource(), + "zitadel_application_key": application_key.GetResource(), + "zitadel_project_grant": project_grant.GetResource(), + "zitadel_user_grant": user_grant.GetResource(), + "zitadel_org_member": org_member.GetResource(), + "zitadel_instance_member": instance_member.GetResource(), + "zitadel_project_member": project_member.GetResource(), + "zitadel_project_grant_member": project_grant_member.GetResource(), + "zitadel_domain_policy": domain_policy.GetResource(), + "zitadel_label_policy": label_policy.GetResource(), + "zitadel_lockout_policy": lockout_policy.GetResource(), + "zitadel_login_policy": login_policy.GetResource(), + "zitadel_password_complexity_policy": password_complexity_policy.GetResource(), + "zitadel_privacy_policy": privacy_policy.GetResource(), + "zitadel_trigger_actions": trigger_actions.GetResource(), + "zitadel_personal_access_token": pat.GetResource(), + "zitadel_machine_key": machine_key.GetResource(), + "zitadel_default_label_policy": default_label_policy.GetResource(), + "zitadel_default_login_policy": default_login_policy.GetResource(), + "zitadel_default_lockout_policy": default_lockout_policy.GetResource(), + "zitadel_default_domain_policy": default_domain_policy.GetResource(), + "zitadel_default_privacy_policy": default_privacy_policy.GetResource(), + "zitadel_default_password_complexity_policy": default_password_complexity_policy.GetResource(), + "zitadel_sms_provider_twilio": sms_provider_twilio.GetResource(), + "zitadel_smtp_config": smtp_config.GetResource(), + "zitadel_default_notification_policy": default_notification_policy.GetResource(), + "zitadel_notification_policy": notification_policy.GetResource(), + "zitadel_idp_github": idp_github.GetResource(), + "zitadel_idp_github_es": idp_github_es.GetResource(), + "zitadel_idp_gitlab": idp_gitlab.GetResource(), + "zitadel_idp_gitlab_self_hosted": idp_gitlab_self_hosted.GetResource(), + "zitadel_idp_google": idp_google.GetResource(), + "zitadel_idp_azure_ad": idp_azure_ad.GetResource(), + "zitadel_idp_ldap": idp_ldap.GetResource(), + "zitadel_org_idp_jwt": org_idp_jwt.GetResource(), + "zitadel_org_idp_oidc": org_idp_oidc.GetResource(), + "zitadel_org_idp_github": org_idp_github.GetResource(), + "zitadel_org_idp_github_es": org_idp_github_es.GetResource(), + "zitadel_org_idp_gitlab": org_idp_gitlab.GetResource(), + "zitadel_org_idp_gitlab_self_hosted": org_idp_gitlab_self_hosted.GetResource(), + "zitadel_org_idp_google": org_idp_google.GetResource(), + "zitadel_org_idp_azure_ad": org_idp_azure_ad.GetResource(), + "zitadel_org_idp_ldap": org_idp_ldap.GetResource(), + "zitadel_default_oidc_settings": default_oidc_settings.GetResource(), }, - ConfigureContextFunc: providerConfigure, + ConfigureContextFunc: ProviderConfigure, } } -func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { - clientinfo, err := v2.GetClientInfo(d) +func ProviderConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { + clientinfo, err := helper.GetClientInfo( + d.Get(helper.InsecureVar).(bool), + d.Get(helper.DomainVar).(string), + d.Get(helper.TokenVar).(string), + d.Get(helper.JWTProfileFile).(string), + d.Get(helper.JWTProfileJSON).(string), + d.Get(helper.PortVar).(string), + ) if err != nil { return nil, diag.FromErr(err) } diff --git a/zitadel/sms_provider_twilio/const.go b/zitadel/sms_provider_twilio/const.go new file mode 100644 index 00000000..faf544ed --- /dev/null +++ b/zitadel/sms_provider_twilio/const.go @@ -0,0 +1,8 @@ +package sms_provider_twilio + +const ( + providerIDVar = "provider_id" + sidVar = "sid" + TokenVar = "token" + SenderNumberVar = "sender_number" +) diff --git a/zitadel/sms_provider_twilio/funcs.go b/zitadel/sms_provider_twilio/funcs.go new file mode 100644 index 00000000..3d89be17 --- /dev/null +++ b/zitadel/sms_provider_twilio/funcs.go @@ -0,0 +1,135 @@ +package sms_provider_twilio + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveSMSProvider(ctx, &admin.RemoveSMSProviderRequest{Id: d.Id()}) + if err != nil { + return diag.Errorf("failed to delete sms provider twilio: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.AddSMSProviderTwilio(ctx, &admin.AddSMSProviderTwilioRequest{ + Sid: d.Get(sidVar).(string), + Token: d.Get(TokenVar).(string), + SenderNumber: d.Get(SenderNumberVar).(string), + }) + if err != nil { + return diag.Errorf("failed to create sms provider twilio: %v", err) + } + d.SetId(resp.Id) + + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChanges(SenderNumberVar, sidVar) { + _, err = client.UpdateSMSProviderTwilio(ctx, &admin.UpdateSMSProviderTwilioRequest{ + Id: d.Id(), + Sid: d.Get(sidVar).(string), + SenderNumber: d.Get(SenderNumberVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update sms provider twilio: %v", err) + } + } + + if d.HasChange(TokenVar) { + _, err = client.UpdateSMSProviderTwilioToken(ctx, &admin.UpdateSMSProviderTwilioTokenRequest{ + Id: d.Id(), + Token: d.Get(TokenVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update sms provider twilio: %v", err) + } + } + + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetSMSProvider(ctx, &admin.GetSMSProviderRequest{ + Id: d.Id(), + }) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get sms provider twilio") + } + + set := map[string]interface{}{ + sidVar: resp.GetConfig().GetTwilio().GetSid(), + SenderNumberVar: resp.GetConfig().GetTwilio().GetSenderNumber(), + } + if token, ok := d.GetOk(TokenVar); ok { + set[TokenVar] = token + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of sms provider twilio: %v", k, err) + } + } + d.SetId(resp.Config.Id) + return nil +} diff --git a/zitadel/sms_provider_twilio/resource.go b/zitadel/sms_provider_twilio/resource.go new file mode 100644 index 00000000..b13d4657 --- /dev/null +++ b/zitadel/sms_provider_twilio/resource.go @@ -0,0 +1,36 @@ +package sms_provider_twilio + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the SMS provider Twilio configuration of an instance.", + Schema: map[string]*schema.Schema{ + sidVar: { + Type: schema.TypeString, + Required: true, + Description: "SID used to communicate with Twilio.", + }, + TokenVar: { + Type: schema.TypeString, + Required: true, + Description: "Token used to communicate with Twilio.", + Sensitive: true, + }, + SenderNumberVar: { + Type: schema.TypeString, + Required: true, + Description: "Sender number which is used to send the SMS.", + }, + }, + CreateContext: create, + DeleteContext: delete, + ReadContext: read, + UpdateContext: update, + Importer: helper.ImportWithIDAndOptionalSecret(providerIDVar, TokenVar), + } +} diff --git a/zitadel/sms_provider_twilio/resource_test.go b/zitadel/sms_provider_twilio/resource_test.go new file mode 100644 index 00000000..82ef2654 --- /dev/null +++ b/zitadel/sms_provider_twilio/resource_test.go @@ -0,0 +1,53 @@ +package sms_provider_twilio_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/sms_provider_twilio" +) + +func TestAccSMSProviderTwilio(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_sms_provider_twilio") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, sms_provider_twilio.SenderNumberVar, exampleAttributes).AsString() + exampleSecret := test_utils.AttributeValue(t, sms_provider_twilio.TokenVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, exampleSecret), + exampleProperty, "987654321", + sms_provider_twilio.TokenVar, exampleSecret, "updatedSecret", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckNothing, + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, sms_provider_twilio.TokenVar), + ), + ) +} + +func checkRemoteProperty(frame test_utils.InstanceTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetSMSProvider(frame, &admin.GetSMSProviderRequest{Id: frame.State(state).ID}) + if err != nil { + return fmt.Errorf("getting sms provider failed: %w", err) + } + actual := resp.GetConfig().GetTwilio().GetSenderNumber() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/smtp_config/const.go b/zitadel/smtp_config/const.go new file mode 100644 index 00000000..df0eb6bb --- /dev/null +++ b/zitadel/smtp_config/const.go @@ -0,0 +1,10 @@ +package smtp_config + +const ( + SenderAddressVar = "sender_address" + SenderNameVar = "sender_name" + tlsVar = "tls" + hostVar = "host" + userVar = "user" + PasswordVar = "password" +) diff --git a/zitadel/smtp_config/funcs.go b/zitadel/smtp_config/funcs.go new file mode 100644 index 00000000..3276e27f --- /dev/null +++ b/zitadel/smtp_config/funcs.go @@ -0,0 +1,140 @@ +package smtp_config + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveSMTPConfig(ctx, &admin.RemoveSMTPConfigRequest{}) + if err != nil { + return diag.Errorf("failed to delete smtp config: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + req := &admin.AddSMTPConfigRequest{ + SenderAddress: d.Get(SenderAddressVar).(string), + SenderName: d.Get(SenderNameVar).(string), + Host: d.Get(hostVar).(string), + User: d.Get(userVar).(string), + Tls: d.Get(tlsVar).(bool), + Password: d.Get(PasswordVar).(string), + } + + resp, err := client.AddSMTPConfig(ctx, req) + if err != nil { + return diag.Errorf("failed to create smtp config: %v", err) + } + d.SetId(resp.Details.ResourceOwner) + + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChanges(SenderAddressVar, SenderNameVar, tlsVar, hostVar, userVar) { + _, err = client.UpdateSMTPConfig(ctx, &admin.UpdateSMTPConfigRequest{ + SenderAddress: d.Get(SenderAddressVar).(string), + SenderName: d.Get(SenderNameVar).(string), + Host: d.Get(hostVar).(string), + Tls: d.Get(tlsVar).(bool), + User: d.Get(userVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update smtp config: %v", err) + } + } + + if d.HasChange(PasswordVar) { + _, err = client.UpdateSMTPConfigPassword(ctx, &admin.UpdateSMTPConfigPasswordRequest{ + Password: d.Get(PasswordVar).(string), + }) + if err != nil { + return diag.Errorf("failed to update smtp config password: %v", err) + } + } + + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetAdminClient(clientinfo) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.GetSMTPConfig(ctx, &admin.GetSMTPConfigRequest{}) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get smtp config") + } + + set := map[string]interface{}{ + SenderAddressVar: resp.GetSmtpConfig().GetSenderAddress(), + SenderNameVar: resp.GetSmtpConfig().GetSenderName(), + tlsVar: resp.GetSmtpConfig().GetTls(), + hostVar: resp.GetSmtpConfig().GetHost(), + userVar: resp.GetSmtpConfig().GetUser(), + PasswordVar: d.Get(PasswordVar).(string), + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of smtp config: %v", k, err) + } + } + d.SetId(resp.SmtpConfig.Details.ResourceOwner) + return nil +} diff --git a/zitadel/smtp_config/resource.go b/zitadel/smtp_config/resource.go new file mode 100644 index 00000000..7e0e9405 --- /dev/null +++ b/zitadel/smtp_config/resource.go @@ -0,0 +1,51 @@ +package smtp_config + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the SMTP configuration of an instance.", + Schema: map[string]*schema.Schema{ + SenderAddressVar: { + Type: schema.TypeString, + Required: true, + Description: "Address used to send emails.", + }, + SenderNameVar: { + Type: schema.TypeString, + Required: true, + Description: "Sender name used to send emails.", + }, + tlsVar: { + Type: schema.TypeBool, + Optional: true, + Description: "TLS used to communicate with your SMTP server.", + }, + hostVar: { + Type: schema.TypeString, + Required: true, + Description: "Host and port address to your SMTP server.", + }, + userVar: { + Type: schema.TypeString, + Optional: true, + Description: "User used to communicate with your SMTP server.", + }, + PasswordVar: { + Type: schema.TypeString, + Optional: true, + Description: "Password used to communicate with your SMTP server.", + Sensitive: true, + }, + }, + CreateContext: create, + DeleteContext: delete, + ReadContext: read, + UpdateContext: update, + Importer: helper.ImportWithEmptyID(helper.NewImportAttribute(PasswordVar, helper.ConvertNonEmpty, true)), + } +} diff --git a/zitadel/smtp_config/resource_test.go b/zitadel/smtp_config/resource_test.go new file mode 100644 index 00000000..36624d7e --- /dev/null +++ b/zitadel/smtp_config/resource_test.go @@ -0,0 +1,59 @@ +package smtp_config_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/smtp_config" +) + +func TestAccSMTPConfig(t *testing.T) { + frame := test_utils.NewInstanceTestFrame(t, "zitadel_smtp_config") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + senderAddressProperty := test_utils.AttributeValue(t, smtp_config.SenderAddressVar, exampleAttributes).AsString() + resourceExample = strings.Replace(resourceExample, senderAddressProperty, fmt.Sprintf("zitadel@%s", frame.InstanceDomain), 1) + exampleProperty := test_utils.AttributeValue(t, smtp_config.SenderNameVar, exampleAttributes).AsString() + exampleSecret := test_utils.AttributeValue(t, smtp_config.PasswordVar, exampleAttributes).AsString() + // TODO: Does not work yet, because the smtp config is not deleted (API bug?) + if _, err := frame.RemoveSMTPConfig(frame, &admin.RemoveSMTPConfigRequest{}); err != nil && status.Code(err) != codes.NotFound { + t.Fatalf("failed to remove smtp config: %v", err) + } + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + nil, + test_utils.ReplaceAll(resourceExample, exampleProperty, exampleSecret), + exampleProperty, "updatedProperty", + smtp_config.PasswordVar, exampleSecret, "updatedSecret", + false, + checkRemoteProperty(*frame), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckNothing, + test_utils.ImportStateAttribute(frame.BaseTestFrame, smtp_config.PasswordVar), + ) +} + +func checkRemoteProperty(frame test_utils.InstanceTestFrame) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetSMTPConfig(frame, &admin.GetSMTPConfigRequest{}) + if err != nil { + return fmt.Errorf("getting smtp config failed: %w", err) + } + actual := resp.GetSmtpConfig().GetSenderName() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/trigger_actions/const.go b/zitadel/trigger_actions/const.go new file mode 100644 index 00000000..4023b0d5 --- /dev/null +++ b/zitadel/trigger_actions/const.go @@ -0,0 +1,7 @@ +package trigger_actions + +const ( + FlowTypeVar = "flow_type" + TriggerTypeVar = "trigger_type" + actionsVar = "action_ids" +) diff --git a/zitadel/trigger_actions/datasource.go b/zitadel/trigger_actions/datasource.go new file mode 100644 index 00000000..345639d3 --- /dev/null +++ b/zitadel/trigger_actions/datasource.go @@ -0,0 +1,35 @@ +package trigger_actions + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetDatasource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing triggers, when actions get started", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + FlowTypeVar: { + Type: schema.TypeString, + Required: true, + Description: "Type of the flow to which the action triggers belong", + }, + TriggerTypeVar: { + Type: schema.TypeString, + Required: true, + Description: "Trigger type on when the actions get triggered", + }, + actionsVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + Description: "IDs of the triggered actions", + }, + }, + ReadContext: read, + } +} diff --git a/zitadel/trigger_actions/funcs.go b/zitadel/trigger_actions/funcs.go new file mode 100644 index 00000000..27a81289 --- /dev/null +++ b/zitadel/trigger_actions/funcs.go @@ -0,0 +1,137 @@ +package trigger_actions + +import ( + "context" + "strconv" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + flowType := d.Get(FlowTypeVar).(string) + flowTypeValues := helper.EnumValueMap(FlowTypes()) + triggerType := d.Get(TriggerTypeVar).(string) + triggerTypeValues := helper.EnumValueMap(TriggerTypes()) + _, err = client.SetTriggerActions(ctx, &management.SetTriggerActionsRequest{ + FlowType: strconv.Itoa(int(flowTypeValues[flowType])), + TriggerType: strconv.Itoa(int(triggerTypeValues[triggerType])), + ActionIds: []string{}, + }) + if helper.IgnoreIfNotFoundError(err) != nil { + return diag.Errorf("failed to delete trigger actions: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + flowType := d.Get(FlowTypeVar).(string) + flowTypeValues := helper.EnumValueMap(FlowTypes()) + triggerType := d.Get(TriggerTypeVar).(string) + triggerTypeValues := helper.EnumValueMap(TriggerTypes()) + _, err = client.SetTriggerActions(ctx, &management.SetTriggerActionsRequest{ + FlowType: strconv.Itoa(int(flowTypeValues[flowType])), + TriggerType: strconv.Itoa(int(triggerTypeValues[triggerType])), + ActionIds: helper.GetOkSetToStringSlice(d, actionsVar), + }) + if err != nil { + return diag.Errorf("failed to update trigger actions: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + orgID := d.Get(helper.OrgIDVar).(string) + client, err := helper.GetManagementClient(clientinfo, orgID) + if err != nil { + return diag.FromErr(err) + } + flowType := d.Get(FlowTypeVar).(string) + flowTypeValues := helper.EnumValueMap(FlowTypes()) + triggerType := d.Get(TriggerTypeVar).(string) + triggerTypeValues := helper.EnumValueMap(TriggerTypes()) + actionIDs := helper.GetOkSetToStringSlice(d, actionsVar) + _, err = client.SetTriggerActions(ctx, &management.SetTriggerActionsRequest{ + FlowType: strconv.Itoa(int(flowTypeValues[flowType])), + TriggerType: strconv.Itoa(int(triggerTypeValues[triggerType])), + ActionIds: actionIDs, + }) + if err != nil { + return diag.Errorf("failed to create trigger actions: %v", err) + } + d.SetId(getTriggerActionsID(orgID, flowType, triggerType)) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + orgID := d.Get(helper.OrgIDVar).(string) + flowType := d.Get(FlowTypeVar).(string) + triggerType := d.Get(TriggerTypeVar).(string) + client, err := helper.GetManagementClient(clientinfo, orgID) + if err != nil { + return diag.FromErr(err) + } + flowTypeValues := helper.EnumValueMap(FlowTypes()) + triggerTypeNames := TriggerTypes() + resp, err := client.GetFlow(ctx, &management.GetFlowRequest{Type: strconv.Itoa(int(flowTypeValues[flowType]))}) + if err != nil { + return diag.FromErr(err) + } + var actionIDs []string + for _, triggerAction := range resp.GetFlow().GetTriggerActions() { + triggerTypeID, err := strconv.Atoi(triggerAction.GetTriggerType().GetId()) + if err != nil { + return diag.FromErr(err) + } + if triggerTypeNames[int32(triggerTypeID)] != triggerType { + continue + } + for _, action := range triggerAction.GetActions() { + actionIDs = append(actionIDs, action.GetId()) + } + } + if err = d.Set(actionsVar, actionIDs); err != nil { + return diag.Errorf("setting action ids %s to property %s failed: %v", actionIDs, actionsVar, err) + } + d.SetId(getTriggerActionsID(orgID, flowType, triggerType)) + return nil +} + +func getTriggerActionsID(orgID, flowType string, triggerType string) string { + return orgID + "_" + flowType + "_" + triggerType +} diff --git a/zitadel/trigger_actions/resource.go b/zitadel/trigger_actions/resource.go new file mode 100644 index 00000000..507aa873 --- /dev/null +++ b/zitadel/trigger_actions/resource.go @@ -0,0 +1,70 @@ +package trigger_actions + +import ( + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing triggers, when actions get started", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + FlowTypeVar: { + Type: schema.TypeString, + Required: true, + Description: "Type of the flow to which the action triggers belong" + helper.DescriptionEnumValuesList(FlowTypes()), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(FlowTypeVar, value, helper.EnumValueMap(FlowTypes())) + }, + ForceNew: true, + }, + TriggerTypeVar: { + Type: schema.TypeString, + Required: true, + Description: "Trigger type on when the actions get triggered" + helper.DescriptionEnumValuesList(TriggerTypes()), + ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics { + return helper.EnumValueValidation(TriggerTypeVar, value, helper.EnumValueMap(TriggerTypes())) + }, + ForceNew: true, + }, + actionsVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + Description: "IDs of the triggered actions", + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithEmptyID( + helper.NewImportAttribute(FlowTypeVar, helper.ConvertNonEmpty, false), + helper.NewImportAttribute(TriggerTypeVar, helper.ConvertNonEmpty, false), + helper.ImportOptionalOrgAttribute, + ), + } +} + +func FlowTypes() map[int32]string { + return map[int32]string{ + 1: "FLOW_TYPE_EXTERNAL_AUTHENTICATION", + 2: "FLOW_TYPE_CUSTOMISE_TOKEN", + 3: "FLOW_TYPE_INTERNAL_AUTHENTICATION", + } +} +func TriggerTypes() map[int32]string { + return map[int32]string{ + 1: "TRIGGER_TYPE_POST_AUTHENTICATION", + 2: "TRIGGER_TYPE_PRE_CREATION", + 3: "TRIGGER_TYPE_POST_CREATION", + 4: "TRIGGER_TYPE_PRE_USERINFO_CREATION", + 5: "TRIGGER_TYPE_PRE_ACCESS_TOKEN_CREATION", + } +} diff --git a/zitadel/trigger_actions/resource_test.go b/zitadel/trigger_actions/resource_test.go new file mode 100644 index 00000000..ee58a21d --- /dev/null +++ b/zitadel/trigger_actions/resource_test.go @@ -0,0 +1,69 @@ +package trigger_actions_test + +import ( + "fmt" + "regexp" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/action/action_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/trigger_actions" +) + +func TestAccTriggerActions(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_trigger_actions") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, trigger_actions.TriggerTypeVar, exampleAttributes).AsString() + flowType := test_utils.AttributeValue(t, trigger_actions.FlowTypeVar, exampleAttributes).AsString() + actionDep, _ := action_test_dep.Create(t, frame) + updatedProperty := "TRIGGER_TYPE_PRE_USERINFO_CREATION" + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, actionDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + false, + checkRemoteProperty(*frame, flowType), + regexp.MustCompile(fmt.Sprintf("^%s_([A-Z_]+)_(%s|%s)$", helper.ZitadelGeneratedIdPattern, exampleProperty, updatedProperty)), + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(*frame, flowType), exampleProperty), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportStateAttribute(frame.BaseTestFrame, trigger_actions.FlowTypeVar), + test_utils.ImportStateAttribute(frame.BaseTestFrame, trigger_actions.TriggerTypeVar), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame, flowType string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + flowTypeValues := helper.EnumValueMap(trigger_actions.FlowTypes()) + resp, err := frame.GetFlow(frame, &management.GetFlowRequest{Type: strconv.Itoa(int(flowTypeValues[flowType]))}) + if err != nil { + return fmt.Errorf("flow type not found: %w", err) + } + typesMapping := trigger_actions.TriggerTypes() + var foundTypes []string + for _, actual := range resp.GetFlow().GetTriggerActions() { + idInt, err := strconv.Atoi(actual.GetTriggerType().GetId()) + if err != nil { + return err + } + foundType := typesMapping[int32(idInt)] + foundTypes = append(foundTypes, foundType) + if foundType == expect { + return nil + } + } + return fmt.Errorf("expected trigger type %s not found in %v: %w", expect, foundTypes, test_utils.ErrNotFound) + } + } +} diff --git a/zitadel/user_grant/const.go b/zitadel/user_grant/const.go new file mode 100644 index 00000000..53b377e1 --- /dev/null +++ b/zitadel/user_grant/const.go @@ -0,0 +1,9 @@ +package user_grant + +const ( + grantIDVar = "grant_id" + projectIDVar = "project_id" + projectGrantIDVar = "project_grant_id" + UserIDVar = "user_id" + RoleKeysVar = "role_keys" +) diff --git a/zitadel/user_grant/funcs.go b/zitadel/user_grant/funcs.go new file mode 100644 index 00000000..d37d8b3e --- /dev/null +++ b/zitadel/user_grant/funcs.go @@ -0,0 +1,127 @@ +package user_grant + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started delete") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.RemoveUserGrant(ctx, &management.RemoveUserGrantRequest{ + GrantId: d.Id(), + UserId: d.Get(UserIDVar).(string), + }) + if err != nil { + return diag.Errorf("failed to delete usergrant: %v", err) + } + return nil +} + +func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started update") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateUserGrant(ctx, &management.UpdateUserGrantRequest{ + GrantId: d.Id(), + UserId: d.Get(UserIDVar).(string), + RoleKeys: helper.GetOkSetToStringSlice(d, RoleKeysVar), + }) + if err != nil { + return diag.Errorf("failed to update usergrant: %v", err) + } + return nil +} + +func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started create") + + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + + resp, err := client.AddUserGrant(ctx, &management.AddUserGrantRequest{ + UserId: d.Get(UserIDVar).(string), + ProjectGrantId: d.Get(projectGrantIDVar).(string), + ProjectId: d.Get(projectIDVar).(string), + RoleKeys: helper.GetOkSetToStringSlice(d, RoleKeysVar), + }) + if err != nil { + return diag.Errorf("failed to create usergrant: %v", err) + } + d.SetId(resp.GetUserGrantId()) + return nil +} + +func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + tflog.Info(ctx, "started read") + clientinfo, ok := m.(*helper.ClientInfo) + if !ok { + return diag.Errorf("failed to get client") + } + client, err := helper.GetManagementClient(clientinfo, d.Get(helper.OrgIDVar).(string)) + if err != nil { + return diag.FromErr(err) + } + resp, err := client.GetUserGrantByID(ctx, &management.GetUserGrantByIDRequest{ + GrantId: helper.GetID(d, grantIDVar), + UserId: d.Get(UserIDVar).(string), + }) + if err != nil && helper.IgnoreIfNotFoundError(err) == nil { + d.SetId("") + return nil + } + if err != nil { + return diag.Errorf("failed to get user grant") + } + grant := resp.GetUserGrant() + set := map[string]interface{}{ + UserIDVar: grant.GetUserId(), + RoleKeysVar: grant.GetRoleKeys(), + helper.OrgIDVar: grant.GetDetails().GetResourceOwner(), + } + if grant.GetProjectId() != "" { + set[projectIDVar] = grant.GetProjectId() + } + if grant.GetProjectGrantId() != "" { + set[projectGrantIDVar] = grant.GetProjectGrantId() + } + for k, v := range set { + if err := d.Set(k, v); err != nil { + return diag.Errorf("failed to set %s of usergrant: %v", k, err) + } + } + d.SetId(grant.GetId()) + return nil +} diff --git a/zitadel/user_grant/resource.go b/zitadel/user_grant/resource.go new file mode 100644 index 00000000..aaf62e3d --- /dev/null +++ b/zitadel/user_grant/resource.go @@ -0,0 +1,47 @@ +package user_grant + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +func GetResource() *schema.Resource { + return &schema.Resource{ + Description: "Resource representing the authorization given to a user directly, including the given roles.", + Schema: map[string]*schema.Schema{ + helper.OrgIDVar: helper.OrgIDResourceField, + projectIDVar: { + Type: schema.TypeString, + Optional: true, + Description: "ID of the project", + ForceNew: true, + }, + projectGrantIDVar: { + Type: schema.TypeString, + Optional: true, + Description: "ID of the granted project", + ForceNew: true, + }, + UserIDVar: { + Type: schema.TypeString, + Required: true, + Description: "ID of the user", + ForceNew: true, + }, + RoleKeysVar: { + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "List of roles granted", + }, + }, + DeleteContext: delete, + CreateContext: create, + UpdateContext: update, + ReadContext: read, + Importer: helper.ImportWithIDAndOptionalOrg(grantIDVar, helper.NewImportAttribute(UserIDVar, helper.ConvertID, false)), + } +} diff --git a/zitadel/user_grant/resource_test.go b/zitadel/user_grant/resource_test.go new file mode 100644 index 00000000..0d6a882e --- /dev/null +++ b/zitadel/user_grant/resource_test.go @@ -0,0 +1,66 @@ +package user_grant_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/human_user/human_user_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project/project_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/project_role/project_role_test_dep" + "github.com/zitadel/terraform-provider-zitadel/zitadel/user_grant" +) + +func TestAccUserGrant(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_user_grant") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, user_grant.RoleKeysVar, exampleAttributes).AsValueSlice()[0].AsString() + updatedProperty := "updatedProperty" + projectDep, projectID := project_test_dep.Create(t, frame) + project_role_test_dep.Create(t, frame, projectID, exampleProperty, updatedProperty) + userDep, userID := human_user_test_dep.Create(t, frame) + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency, projectDep, userDep}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, updatedProperty, + "", "", "", + true, + checkRemoteProperty(*frame, userID), + helper.ZitadelGeneratedIdOnlyRegex, + test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(*frame, userID), ""), + test_utils.ChainImportStateIdFuncs( + test_utils.ImportResourceId(frame.BaseTestFrame), + test_utils.ImportStateAttribute(frame.BaseTestFrame, user_grant.UserIDVar), + test_utils.ImportOrgId(frame), + ), + ) +} + +func checkRemoteProperty(frame test_utils.OrgTestFrame, userID string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + resp, err := frame.GetUserGrantByID(frame, &management.GetUserGrantByIDRequest{ + UserId: userID, + GrantId: frame.State(state).ID, + }) + if err != nil { + return err + } + actualRoleKeys := resp.GetUserGrant().GetRoleKeys() + if len(actualRoleKeys) != 1 { + return fmt.Errorf("expected 1 role, but got %d", len(actualRoleKeys)) + } + if expect != actualRoleKeys[0] { + return fmt.Errorf("expected role key %s, but got %s", expect, actualRoleKeys[0]) + } + return nil + } + } +} diff --git a/zitadel/v1/action.go b/zitadel/v1/action.go deleted file mode 100644 index b7edc7ef..00000000 --- a/zitadel/v1/action.go +++ /dev/null @@ -1,82 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - actionOrgId = "org_id" - actionState = "state" - actionName = "name" - actionScript = "script" - actionTimeout = "timeout" - actionAllowedToFail = "allowed_to_fail" -) - -func GetActionDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - actionOrgId: { - Type: schema.TypeString, - Computed: true, - Description: "Name of the organization", - }, - actionState: { - Type: schema.TypeInt, - Computed: true, - Description: "the state of the action", - }, - actionName: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - actionScript: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - actionTimeout: { - Type: schema.TypeString, - Computed: true, - Description: "after which time the action will be terminated if not finished", - }, - actionAllowedToFail: { - Type: schema.TypeBool, - Computed: true, - Description: "when true, the next action will be called even if this action fails", - }, - }, - } -} - -func readActionsOfOrg(ctx context.Context, actions *schema.Set, m interface{}, clientinfo *ClientInfo, org string) diag.Diagnostics { - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.ListActions(ctx, &management2.ListActionsRequest{}) - if err != nil { - return diag.Errorf("failed to get list of domains: %v", err) - } - - for i := range resp.Result { - action := resp.Result[i] - - values := map[string]interface{}{ - actionOrgId: action.GetDetails().GetResourceOwner(), - actionState: action.GetState(), - actionName: action.GetName(), - actionScript: action.GetScript(), - actionTimeout: action.GetTimeout().String(), - actionAllowedToFail: action.GetAllowedToFail(), - } - actions.Add(values) - } - - return nil -} diff --git a/zitadel/v1/client.go b/zitadel/v1/client.go deleted file mode 100644 index cd3343de..00000000 --- a/zitadel/v1/client.go +++ /dev/null @@ -1,56 +0,0 @@ -package v1 - -import ( - "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/zitadel/oidc/pkg/oidc" - "github.com/zitadel/zitadel-go/pkg/client/management" - "github.com/zitadel/zitadel-go/pkg/client/middleware" - "github.com/zitadel/zitadel-go/pkg/client/zitadel" -) - -type ClientInfo struct { - Issuer string - Address string - Project string - Token string -} - -func GetClientInfo(d *schema.ResourceData) (*ClientInfo, error) { - issuer := d.Get(issuerVar).(string) - address := d.Get(addressVar).(string) - projectID := d.Get(projectVar).(string) - token := d.Get(tokenVar).(string) - - return &ClientInfo{ - issuer, - address, - projectID, - token, - }, nil -} - -func getManagementClient(clientinfo *ClientInfo, orgID string) (*management.Client, error) { - opts := []zitadel.Option{ - zitadel.WithCustomURL(clientinfo.Issuer, clientinfo.Address), - zitadel.WithJWTProfileTokenSource(middleware.JWTProfileFromPath(clientinfo.Token)), - } - if orgID != "" { - opts = append(opts, zitadel.WithOrgID(orgID)) - } - - client, err := management.NewClient( - []string{oidc.ScopeOpenID, zitadel.ScopeProjectID(clientinfo.Project)}, - opts..., - ) - if err != nil { - return nil, fmt.Errorf("failed to start zitadel client: %v", err) - } - /*defer func() { - err := client.Connection.Close() - if err != nil { - log.Println("could not close grpc connection", err) - } - }()*/ - return client, nil -} diff --git a/zitadel/v1/domain.go b/zitadel/v1/domain.go deleted file mode 100644 index 51e25fed..00000000 --- a/zitadel/v1/domain.go +++ /dev/null @@ -1,79 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - domainOrgIdVar = "org_id" - domainNameVar = "name" - domainIsVerified = "is_verified" - domainIsPrimary = "is_primary" - domainValidationType = "validation_type" -) - -func GetDomainDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - domainNameVar: { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Name of the domain", - }, - domainOrgIdVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization", - }, - domainIsVerified: { - Type: schema.TypeBool, - Optional: true, - Computed: true, - Description: "Is domain verified", - }, - domainIsPrimary: { - Type: schema.TypeBool, - Optional: true, - Computed: true, - Description: "Is domain primary", - }, - domainValidationType: { - Type: schema.TypeInt, - Optional: true, - Computed: true, - Description: "Validation type", - }, - }, - } -} - -func readDomainsOfOrg(ctx context.Context, domains *schema.Set, m interface{}, clientinfo *ClientInfo, org string) diag.Diagnostics { - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.ListOrgDomains(ctx, &management2.ListOrgDomainsRequest{}) - if err != nil { - return diag.Errorf("failed to get list of domains: %v", err) - } - - for i := range resp.Result { - domain := resp.Result[i] - - values := map[string]interface{}{ - domainOrgIdVar: domain.GetOrgId(), - domainNameVar: domain.GetDomainName(), - domainIsVerified: domain.GetIsVerified(), - domainIsPrimary: domain.GetIsPrimary(), - domainValidationType: int(domain.GetValidationType().Number()), - } - domains.Add(values) - } - - return nil -} diff --git a/zitadel/v1/iam_policy.go b/zitadel/v1/iam_policy.go deleted file mode 100644 index 647d0536..00000000 --- a/zitadel/v1/iam_policy.go +++ /dev/null @@ -1,58 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - iamPolicyOrgIdVar = "org_id" - iamPolicyUserLogin = "user_login" - iamPolicyIsDefault = "is_default" -) - -func GetIAMPolicyDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - iamPolicyOrgIdVar: { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Id for the organization", - }, - iamPolicyUserLogin: { - Type: schema.TypeBool, - Computed: true, - Description: "User login must be domain", - }, - iamPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "Is this policy the default", - }, - }, - } -} - -func readIAMPolicyOfOrg(ctx context.Context, policies *schema.Set, m interface{}, clientinfo *ClientInfo, org string) diag.Diagnostics { - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetOrgIAMPolicy(ctx, &management2.GetOrgIAMPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get iam policy: %v", err) - } - - policy := resp.Policy - values := map[string]interface{}{ - iamPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - iamPolicyUserLogin: policy.GetUserLoginMustBeDomain(), - iamPolicyIsDefault: policy.GetIsDefault(), - } - policies.Add(values) - return nil -} diff --git a/zitadel/v1/label_policy.go b/zitadel/v1/label_policy.go deleted file mode 100644 index 4e835a64..00000000 --- a/zitadel/v1/label_policy.go +++ /dev/null @@ -1,156 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - labelPolicyOrgIdVar = "org_id" - labelPolicyPrimaryColor = "primary_color" - labelPolicyIsDefault = "is_default" - labelPolicyHideLoginNameSuffix = "hide_login_name_suffix" - labelPolicyWarnColor = "warn_color" - labelPolicyBackgroundColor = "background_color" - labelPolicyFontColor = "font_color" - labelPolicyPrimaryColorDark = "primary_color_dark" - labelPolicyBackgroundColorDark = "background_color_dark" - labelPolicyWarnColorDark = "warn_color_dark" - labelPolicyFontColorDark = "font_color_dark" - labelPolicyDisableWatermark = "disable_watermark" - labelPolicyLogoURL = "logo_url" - labelPolicyIconURL = "icon_url" - labelPolicyLogoURLDark = "logo_url_dark" - labelPolicyIconURLDark = "icon_url_dark" - labelPolicyFontURL = "font_url" -) - -func GetLabelPolicyDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - labelPolicyOrgIdVar: { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Id for the organization", - }, - labelPolicyPrimaryColor: { - Type: schema.TypeString, - Computed: true, - Description: "hex value for primary color", - }, - labelPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the organisation's admin changed the policy", - }, - labelPolicyHideLoginNameSuffix: { - Type: schema.TypeBool, - Computed: true, - Description: "hides the org suffix on the login form if the scope \"urn:zitadel:iam:org:domain:primary:{domainname}\" is set. Details about this scope in https://docs.zitadel.ch/concepts#Reserved_Scopes", - }, - labelPolicyWarnColor: { - Type: schema.TypeString, - Computed: true, - Description: "hex value for warn color", - }, - labelPolicyBackgroundColor: { - Type: schema.TypeString, - Computed: true, - Description: "hex value for background color", - }, - labelPolicyFontColor: { - Type: schema.TypeString, - Computed: true, - Description: "hex value for font color", - }, - labelPolicyPrimaryColorDark: { - Type: schema.TypeString, - Computed: true, - Description: "hex value for primary color dark theme", - }, - labelPolicyBackgroundColorDark: { - Type: schema.TypeString, - Computed: true, - Description: "hex value for background color dark theme", - }, - labelPolicyWarnColorDark: { - Type: schema.TypeString, - Computed: true, - Description: "hex value for warn color dark theme", - }, - labelPolicyFontColorDark: { - Type: schema.TypeString, - Computed: true, - Description: "hex value for font color dark theme", - }, - labelPolicyDisableWatermark: { - Type: schema.TypeBool, - Computed: true, - Description: "", - }, - labelPolicyLogoURL: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - labelPolicyIconURL: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - labelPolicyLogoURLDark: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - labelPolicyIconURLDark: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - labelPolicyFontURL: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - }, - } -} - -func readLabelPolicyOfOrg(ctx context.Context, policies *schema.Set, m interface{}, clientinfo *ClientInfo, org string) diag.Diagnostics { - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetLabelPolicy(ctx, &management2.GetLabelPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get list of domains: %v", err) - } - - policy := resp.Policy - values := map[string]interface{}{ - labelPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - labelPolicyPrimaryColor: policy.GetPrimaryColor(), - labelPolicyIsDefault: policy.GetIsDefault(), - labelPolicyHideLoginNameSuffix: policy.GetHideLoginNameSuffix(), - labelPolicyWarnColor: policy.GetWarnColor(), - labelPolicyBackgroundColor: policy.GetBackgroundColor(), - labelPolicyFontColor: policy.GetFontColor(), - labelPolicyPrimaryColorDark: policy.GetPrimaryColorDark(), - labelPolicyBackgroundColorDark: policy.GetBackgroundColorDark(), - labelPolicyWarnColorDark: policy.GetWarnColorDark(), - labelPolicyFontColorDark: policy.GetFontColorDark(), - labelPolicyDisableWatermark: policy.GetDisableWatermark(), - labelPolicyLogoURL: policy.GetLogoUrl(), - labelPolicyIconURL: policy.GetIconUrl(), - labelPolicyLogoURLDark: policy.GetLogoUrlDark(), - labelPolicyIconURLDark: policy.GetIconUrlDark(), - labelPolicyFontURL: policy.GetFontUrl(), - } - policies.Add(values) - return nil -} diff --git a/zitadel/v1/lockout_policy.go b/zitadel/v1/lockout_policy.go deleted file mode 100644 index e1a325a4..00000000 --- a/zitadel/v1/lockout_policy.go +++ /dev/null @@ -1,58 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - lockoutPolicyOrgIdVar = "org_id" - lockoutPolicyMaxPasswordAttempts = "max_password_attempts" - lockoutPolicyIsDefault = "is_default" -) - -func GetLockoutPolicyDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - lockoutPolicyOrgIdVar: { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Id for the organization", - }, - lockoutPolicyMaxPasswordAttempts: { - Type: schema.TypeInt, - Computed: true, - Description: "Maximum password check attempts before the account gets locked. Attempts are reset as soon as the password is entered correct or the password is reset.", - }, - lockoutPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the organisation's admin changed the policy", - }, - }, - } -} - -func readLockoutPolicyOfOrg(ctx context.Context, policies *schema.Set, m interface{}, clientinfo *ClientInfo, org string) diag.Diagnostics { - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetLockoutPolicy(ctx, &management2.GetLockoutPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get list of domains: %v", err) - } - - policy := resp.Policy - values := map[string]interface{}{ - lockoutPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - lockoutPolicyIsDefault: policy.GetIsDefault(), - lockoutPolicyMaxPasswordAttempts: policy.GetMaxPasswordAttempts(), - } - policies.Add(values) - return nil -} diff --git a/zitadel/v1/login_policy.go b/zitadel/v1/login_policy.go deleted file mode 100644 index 0992ed28..00000000 --- a/zitadel/v1/login_policy.go +++ /dev/null @@ -1,107 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - loginPolicyOrgIdVar = "org_id" - loginPolicyAllowUsernamePassword = "allow_username_password" - loginPolicyAllowRegister = "allow_register" - loginPolicyAllowExternalIDP = "allow_external_idp" - loginPolicyForceMFA = "force_mfa" - loginPolicyPasswordlessType = "passwordless_type" - loginPolicyIsDefault = "is_default" - loginPolicyHidePasswordReset = "hide_password_reset" - loginPolicyIgnoreUnknownUsernames = "ignore_unknown_usernames" - loginPolicyDefaultRedirectURI = "default_redirect_uri" -) - -func GetLoginPolicyDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - loginPolicyOrgIdVar: { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Id for the organization", - }, - loginPolicyAllowUsernamePassword: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if a user is allowed to login with his username and password", - }, - loginPolicyAllowRegister: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if a person is allowed to register a user on this organisation", - }, - loginPolicyAllowExternalIDP: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if a user is allowed to add a defined identity provider. E.g. Google auth", - }, - loginPolicyForceMFA: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if a user MUST use a multi factor to log in", - }, - loginPolicyPasswordlessType: { - Type: schema.TypeInt, - Computed: true, - Description: "defines if passwordless is allowed for users", - }, - loginPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the organisation's admin changed the policy", - }, - loginPolicyHidePasswordReset: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if password reset link should be shown in the login screen", - }, - loginPolicyIgnoreUnknownUsernames: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if unknown username on login screen directly return an error or always display the password screen", - }, - loginPolicyDefaultRedirectURI: { - Type: schema.TypeString, - Computed: true, - Description: "defines where the user will be redirected to if the login is started without app context (e.g. from mail)", - }, - }, - } -} - -func readLoginPolicyOfOrg(ctx context.Context, policies *schema.Set, m interface{}, clientinfo *ClientInfo, org string) diag.Diagnostics { - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetLoginPolicy(ctx, &management2.GetLoginPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get list of domains: %v", err) - } - - policy := resp.Policy - values := map[string]interface{}{ - loginPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - loginPolicyAllowUsernamePassword: policy.GetAllowUsernamePassword(), - loginPolicyAllowRegister: policy.GetAllowRegister(), - loginPolicyAllowExternalIDP: policy.GetAllowExternalIdp(), - loginPolicyForceMFA: policy.GetForceMfa(), - loginPolicyPasswordlessType: policy.GetPasswordlessType(), - loginPolicyIsDefault: policy.GetIsDefault(), - loginPolicyHidePasswordReset: policy.GetHidePasswordReset(), - loginPolicyIgnoreUnknownUsernames: policy.GetIgnoreUnknownUsernames(), - //loginPolicyDefaultRedirectURI: policy - } - policies.Add(values) - return nil -} diff --git a/zitadel/v1/member.go b/zitadel/v1/member.go deleted file mode 100644 index 0a3f550a..00000000 --- a/zitadel/v1/member.go +++ /dev/null @@ -1,58 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - memberOrgID = "org_id" - memberUserID = "user_id" -) - -func GetMemberDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - memberOrgID: { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "ID of the organization", - }, - memberUserID: { - Type: schema.TypeString, - Required: true, - Description: "ID of the user", - }, - }, - } -} - -func readMembersOfOrg(ctx context.Context, domains *schema.Set, m interface{}, clientinfo *ClientInfo, org string) diag.Diagnostics { - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.ListOrgDomains(ctx, &management2.ListOrgDomainsRequest{}) - if err != nil { - return diag.Errorf("failed to get list of domains: %v", err) - } - - for i := range resp.Result { - domain := resp.Result[i] - - values := map[string]interface{}{ - domainOrgIdVar: domain.GetOrgId(), - domainNameVar: domain.GetDomainName(), - domainIsVerified: domain.GetIsVerified(), - domainIsPrimary: domain.GetIsPrimary(), - domainValidationType: int(domain.GetValidationType().Number()), - } - domains.Add(values) - } - - return nil -} diff --git a/zitadel/v1/org.go b/zitadel/v1/org.go deleted file mode 100644 index f79e4679..00000000 --- a/zitadel/v1/org.go +++ /dev/null @@ -1,294 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - orgVar = "org" - nameVar = "name" - issuerVar = "issuer" - addressVar = "address" - projectVar = "project" - tokenVar = "token" - - passwordComplexityPolicyVar = "password_complexity_policy" - lockoutPolicyVar = "lockout_policy" - loginPolicyVar = "login_policy" - iamPolicyVar = "iam_policy" - labelPolicyVar = "label_policy" - privacyPolicyVar = "privacy_policy" - - usersVar = "users" - projectsVar = "projects" - domainsVar = "domains" - actionsVar = "actions" -) - -func GetOrgDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - orgVar: { - Type: schema.TypeString, - Optional: true, - Description: "ID of the organization", - }, - nameVar: { - Type: schema.TypeString, - Optional: true, - Description: "Name of the organization", - }, - issuerVar: { - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("ISSUER", ""), - }, - addressVar: { - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("ADDRESS", ""), - }, - projectVar: { - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("PROJECT", ""), - }, - tokenVar: { - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("SERVICE_TOKEN", ""), - }, - - passwordComplexityPolicyVar: { - Type: schema.TypeSet, - Elem: GetPasswordComplexityPolicyDatasource(), - Optional: true, - Computed: true, - Description: "List of password complexity policies in organization", - }, - lockoutPolicyVar: { - Type: schema.TypeSet, - Elem: GetLockoutPolicyDatasource(), - Optional: true, - Computed: true, - Description: "List of lockout policies in organization", - }, - loginPolicyVar: { - Type: schema.TypeSet, - Elem: GetLoginPolicyDatasource(), - Optional: true, - Computed: true, - Description: "List of login policies in organization", - }, - labelPolicyVar: { - Type: schema.TypeSet, - Elem: GetLabelPolicyDatasource(), - Optional: true, - Computed: true, - Description: "List of label policies in organization", - }, - iamPolicyVar: { - Type: schema.TypeSet, - Elem: GetIAMPolicyDatasource(), - Optional: true, - Computed: true, - Description: "List of domain policies in organization", - }, - privacyPolicyVar: { - Type: schema.TypeSet, - Elem: GetPrivacyPolicyDatasource(), - Optional: true, - Computed: true, - Description: "List of privacy policies in organization", - }, - - usersVar: { - Type: schema.TypeSet, - Elem: GetUserDatasource(), - Optional: true, - Computed: true, - Description: "List of users in organization", - }, - projectsVar: { - Type: schema.TypeSet, - Elem: GetProjectDatasource(), - Optional: true, - Computed: true, - Description: "List of projects in organization", - }, - domainsVar: { - Type: schema.TypeSet, - Elem: GetDomainDatasource(), - Optional: true, - Computed: true, - Description: "List of domains in organization", - }, - actionsVar: { - Type: schema.TypeSet, - Elem: GetActionDatasource(), - Optional: true, - Computed: true, - Description: "List of actions in organization", - }, - }, - ReadContext: readOrg, - } -} - -func readOrg(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, err := GetClientInfo(d) - if err != nil { - return diag.FromErr(err) - } - - client, err := getManagementClient(clientinfo, "") - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetMyOrg(ctx, &management2.GetMyOrgRequest{}) - if err != nil { - return diag.Errorf("failed to get org: %v", err) - } - id := resp.GetOrg().GetId() - d.SetId(id) - name := resp.GetOrg().GetName() - - tflog.Debug(ctx, "found org", map[string]interface{}{ - "id": id, - "name": name, - }) - - if err := d.Set(nameVar, name); err != nil { - return diag.Errorf("failed to set org name: %v", err) - } - if err := d.Set(orgVar, id); err != nil { - return diag.Errorf("failed to set org: %v", err) - } - d.SetId(id) - - /**************************************************************************************** - Users - */ - users := d.Get(usersVar).(*schema.Set) - if err := readUsersOfOrg(ctx, users, m, clientinfo, resp.GetOrg().GetId()); err != nil { - return err - } - if err := d.Set(usersVar, users); err != nil { - return diag.Errorf("failed to set list of users: %v", err) - } - - /**************************************************************************************** - Projects - */ - projects := d.Get(projectsVar).(*schema.Set) - if err := readProjectsOfOrg(ctx, projects, m, clientinfo, resp.GetOrg().GetId()); err != nil { - return err - } - if err := d.Set(projectsVar, projects); err != nil { - return diag.Errorf("failed to set list of projects: %v", err) - } - /**************************************************************************************** - Domains - */ - domains := d.Get(domainsVar).(*schema.Set) - if err := readDomainsOfOrg(ctx, domains, m, clientinfo, resp.GetOrg().GetId()); err != nil { - return err - } - if err := d.Set(domainsVar, domains); err != nil { - return diag.Errorf("failed to set list of domains: %v", err) - } - - /**************************************************************************************** - iam policy - */ - iamPolicy := d.Get(iamPolicyVar).(*schema.Set) - if err := readIAMPolicyOfOrg(ctx, domains, m, clientinfo, resp.GetOrg().GetId()); err != nil { - return err - } - if err := d.Set(iamPolicyVar, iamPolicy); err != nil { - return diag.Errorf("failed to set list of iam policies: %v", err) - } - - /**************************************************************************************** - label policy - */ - labelPolicy := d.Get(labelPolicyVar).(*schema.Set) - if err := readLabelPolicyOfOrg(ctx, domains, m, clientinfo, resp.GetOrg().GetId()); err != nil { - return err - } - if err := d.Set(labelPolicyVar, labelPolicy); err != nil { - return diag.Errorf("failed to set list of label policies: %v", err) - } - - /**************************************************************************************** - lockout policy - */ - lockoutPolicy := d.Get(lockoutPolicyVar).(*schema.Set) - if err := readLockoutPolicyOfOrg(ctx, domains, m, clientinfo, resp.GetOrg().GetId()); err != nil { - return err - } - if err := d.Set(lockoutPolicyVar, lockoutPolicy); err != nil { - return diag.Errorf("failed to set list of lockout policies: %v", err) - } - - /**************************************************************************************** - login policy - */ - loginPolicy := d.Get(loginPolicyVar).(*schema.Set) - if err := readLoginPolicyOfOrg(ctx, domains, m, clientinfo, resp.GetOrg().GetId()); err != nil { - return err - } - if err := d.Set(loginPolicyVar, loginPolicy); err != nil { - return diag.Errorf("failed to set list of login policies: %v", err) - } - - /**************************************************************************************** - password complexity policy - */ - passwordComplexityPolicy := d.Get(passwordComplexityPolicyVar).(*schema.Set) - if err := readPasswordComplexityPolicyPolicyOfOrg(ctx, domains, m, clientinfo, resp.GetOrg().GetId()); err != nil { - return err - } - if err := d.Set(passwordComplexityPolicyVar, passwordComplexityPolicy); err != nil { - return diag.Errorf("failed to set list of password complexity policies: %v", err) - } - - /**************************************************************************************** - privacy policy - */ - privacyPolicy := d.Get(privacyPolicyVar).(*schema.Set) - if err := readPrivacyPolicyOfOrg(ctx, domains, m, clientinfo, resp.GetOrg().GetId()); err != nil { - return err - } - if err := d.Set(privacyPolicyVar, privacyPolicy); err != nil { - return diag.Errorf("failed to set list of privacy policies: %v", err) - } - - /**************************************************************************************** - actions - */ - actions := d.Get(actionsVar).(*schema.Set) - if err := readActionsOfOrg(ctx, domains, m, clientinfo, resp.GetOrg().GetId()); err != nil { - return err - } - if err := d.Set(actionsVar, actions); err != nil { - return diag.Errorf("failed to set list of actions: %v", err) - } - - return nil -} - -func resourceToValueMap(r *schema.Resource, d *schema.ResourceData) map[string]interface{} { - values := make(map[string]interface{}, 0) - for key := range r.Schema { - values[key] = d.Get(key) - } - return values -} diff --git a/zitadel/v1/password_complexity_policy.go b/zitadel/v1/password_complexity_policy.go deleted file mode 100644 index 90ebaa5a..00000000 --- a/zitadel/v1/password_complexity_policy.go +++ /dev/null @@ -1,86 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - passwordCompPolicyOrgIdVar = "org_id" - passwordCompPolicyMinLength = "min_length" - passwordCompPolicyHasUppercase = "has_uppercase" - passwordCompPolicyHasLowercase = "has_lowercase" - passwordCompPolicyHasNumber = "has_number" - passwordCompPolicyHasSymbol = "has_symbol" - passwordCompPolicyIsDefault = "is_default" -) - -func GetPasswordComplexityPolicyDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - passwordCompPolicyOrgIdVar: { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Id for the organization", - }, - passwordCompPolicyMinLength: { - Type: schema.TypeInt, - Computed: true, - Description: "Minimal length for the password", - }, - passwordCompPolicyHasUppercase: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the password MUST contain an upper case letter", - }, - passwordCompPolicyHasLowercase: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the password MUST contain a lower case letter", - }, - passwordCompPolicyHasNumber: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the password MUST contain a number", - }, - passwordCompPolicyHasSymbol: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the password MUST contain a symbol. E.g. \"$\"", - }, - passwordCompPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the organisation's admin changed the policy", - }, - }, - } -} - -func readPasswordComplexityPolicyPolicyOfOrg(ctx context.Context, policies *schema.Set, m interface{}, clientinfo *ClientInfo, org string) diag.Diagnostics { - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetPasswordComplexityPolicy(ctx, &management2.GetPasswordComplexityPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get list of domains: %v", err) - } - - policy := resp.Policy - values := map[string]interface{}{ - passwordCompPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - passwordCompPolicyMinLength: policy.GetMinLength(), - passwordCompPolicyHasUppercase: policy.GetHasUppercase(), - passwordCompPolicyHasLowercase: policy.GetHasLowercase(), - passwordCompPolicyHasNumber: policy.GetHasNumber(), - passwordCompPolicyHasSymbol: policy.GetHasSymbol(), - passwordCompPolicyIsDefault: policy.GetIsDefault(), - } - policies.Add(values) - return nil -} diff --git a/zitadel/v1/privacy_policy.go b/zitadel/v1/privacy_policy.go deleted file mode 100644 index 25233bb1..00000000 --- a/zitadel/v1/privacy_policy.go +++ /dev/null @@ -1,72 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - privacyPolicyOrgIdVar = "org_id" - privacyPolicyTOSLink = "tos_link" - privacyPolicyPrivacyLink = "privacy_link" - privacyPolicyIsDefault = "is_default" - privacyPolicyHelpLink = "help_link" -) - -func GetPrivacyPolicyDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - privacyPolicyOrgIdVar: { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Id for the organization", - }, - privacyPolicyTOSLink: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - privacyPolicyPrivacyLink: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - privacyPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "", - }, - privacyPolicyHelpLink: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - }, - } -} - -func readPrivacyPolicyOfOrg(ctx context.Context, policies *schema.Set, m interface{}, clientinfo *ClientInfo, org string) diag.Diagnostics { - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetPrivacyPolicy(ctx, &management2.GetPrivacyPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get list of domains: %v", err) - } - - policy := resp.Policy - values := map[string]interface{}{ - privacyPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - privacyPolicyTOSLink: policy.GetTosLink(), - privacyPolicyPrivacyLink: policy.GetPrivacyLink(), - privacyPolicyIsDefault: policy.GetIsDefault(), - privacyPolicyHelpLink: policy.GetHelpLink(), - } - policies.Add(values) - return nil -} diff --git a/zitadel/v1/project.go b/zitadel/v1/project.go deleted file mode 100644 index 921a049f..00000000 --- a/zitadel/v1/project.go +++ /dev/null @@ -1,136 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - projectIdVar = "id" - projectNameVar = "name" - projectState = "state" - projectResourceOwner = "resource_owner" - projectRoleAssertionVar = "project_role_assertion" - projectRoleCheckVar = "project_role_check" - hasProjectCheckVar = "has_project_check" - privateLabelingSettingVar = "private_labeling_setting" -) - -func GetProjectDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - projectIdVar: { - Type: schema.TypeString, - Computed: true, - Description: "ID of the project", - }, - projectNameVar: { - Type: schema.TypeString, - Computed: true, - Description: "Name of the project", - }, - projectResourceOwner: { - Type: schema.TypeString, - Computed: true, - Description: "Organization in which the project is located", - }, - projectState: { - Type: schema.TypeInt, - Computed: true, - Description: "State of the project", - }, - projectRoleAssertionVar: { - Type: schema.TypeBool, - Computed: true, - Description: "describes if roles of user should be added in token", - }, - projectRoleCheckVar: { - Type: schema.TypeBool, - Computed: true, - Description: "ZITADEL checks if the user has at least one on this project", - }, - hasProjectCheckVar: { - Type: schema.TypeBool, - Computed: true, - Description: "ZITADEL checks if the org of the user has permission to this project", - }, - privateLabelingSettingVar: { - Type: schema.TypeInt, - Computed: true, - Description: "Defines from where the private labeling should be triggered", - }, - }, - } -} - -func readProjectsOfOrg(ctx context.Context, projects *schema.Set, m interface{}, clientinfo *ClientInfo, org string) diag.Diagnostics { - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.ListProjects(ctx, &management2.ListProjectsRequest{}) - if err != nil { - return diag.Errorf("failed to get list of projects: %v", err) - } - - projectResource := GetProjectDatasource() - for i := range resp.Result { - project := resp.Result[i] - projectdata := projectResource.Data(&terraform.InstanceState{}) - projectdata.SetId(project.Id) - if errDiag := readProject(ctx, projectdata, m, clientinfo, org); errDiag != nil { - return errDiag - } - - data := resourceToValueMap(projectResource, projectdata) - projects.Add(data) - } - return nil -} - -func readProject(ctx context.Context, d *schema.ResourceData, m interface{}, info *ClientInfo, org string) diag.Diagnostics { - tflog.Info(ctx, "started read") - - client, err := getManagementClient(info, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetProjectByID(ctx, &management2.GetProjectByIDRequest{Id: d.Id()}) - if err != nil { - return diag.Errorf("failed to get list of users: %v", err) - } - - project := resp.GetProject() - set := map[string]interface{}{ - projectIdVar: project.GetId(), - projectResourceOwner: project.GetDetails().GetResourceOwner(), - projectState: project.GetState().Number(), - projectNameVar: project.GetName(), - projectRoleAssertionVar: project.GetProjectRoleAssertion(), - projectRoleCheckVar: project.GetProjectRoleCheck(), - hasProjectCheckVar: project.GetHasProjectCheck(), - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of project: %v", k, err) - } - } - d.SetId(project.GetId()) - return nil -} - -func getProjectValueMap(d *schema.ResourceData) map[string]interface{} { - res := GetProjectDatasource() - - values := make(map[string]interface{}, 0) - for key := range res.Schema { - values[key] = d.Get(key) - } - return values -} diff --git a/zitadel/v1/user.go b/zitadel/v1/user.go deleted file mode 100644 index bf51e54e..00000000 --- a/zitadel/v1/user.go +++ /dev/null @@ -1,236 +0,0 @@ -package v1 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - v2 "github.com/zitadel/terraform-provider-zitadel/zitadel/v2" - management2 "github.com/zitadel/zitadel-go/pkg/client/zitadel/management" -) - -const ( - idVar = "id" - resourceOwnerVar = "resource_owner" - userStateVar = "state" - userNameVar = "user_name" - loginNamesVar = "login_names" - preferredLoginNameVar = "preferred_login_name" - typeVar = "type" - - firstNameVar = "first_name" - lastNameVar = "last_name" - nickNameVar = "nick_name" - displayNameVar = "display_name" - preferredLanguageVar = "preferred_language" - genderVar = "gender" - - isEmailVerifiedVar = "is_email_verified" - emailVar = "email" - - isPhoneVerifiedVar = "is_phone_verified" - phoneVar = "phone" - - machineNameVar = "name" - descriptionVar = "description" -) - -func GetUserDatasource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - idVar: { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "ID of the user", - }, - resourceOwnerVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization", - }, - userStateVar: { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "State of the user", - }, - userNameVar: { - Type: schema.TypeString, - Computed: true, - Description: "Username", - }, - loginNamesVar: { - Type: schema.TypeList, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Computed: true, - Description: "Loginnames", - }, - preferredLoginNameVar: { - Type: schema.TypeString, - Computed: true, - Description: "Preferred login name", - }, - typeVar: { - Type: schema.TypeString, - Computed: true, - Description: "Type of the user", - }, - - firstNameVar: { - Type: schema.TypeString, - Computed: true, - Description: "First name of the user", - }, - lastNameVar: { - Type: schema.TypeString, - Computed: true, - Description: "Last name of the user", - }, - nickNameVar: { - Type: schema.TypeString, - Computed: true, - Description: "Nick name of the user", - }, - displayNameVar: { - Type: schema.TypeString, - Computed: true, - Description: "DIsplay name of the user", - }, - preferredLanguageVar: { - Type: schema.TypeString, - Computed: true, - Description: "Preferred language of the user", - }, - genderVar: { - Type: schema.TypeInt, - Computed: true, - Description: "Gender of the user", - }, - - emailVar: { - Type: schema.TypeString, - Computed: true, - Description: "Email of the user", - }, - isEmailVerifiedVar: { - Type: schema.TypeBool, - Computed: true, - Description: "Is the email verified of the user", - }, - - phoneVar: { - Type: schema.TypeString, - Computed: true, - Description: "Phone of the user", - }, - isPhoneVerifiedVar: { - Type: schema.TypeBool, - Computed: true, - Description: "Is the phone verified of the user", - }, - - machineNameVar: { - Type: schema.TypeString, - Computed: true, - Description: "Name of the machine user", - }, - descriptionVar: { - Type: schema.TypeString, - Computed: true, - Description: "Description of the user", - }, - }, - } -} - -func readUsersOfOrg(ctx context.Context, users *schema.Set, m interface{}, clientinfo *ClientInfo, org string) diag.Diagnostics { - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.ListUsers(ctx, &management2.ListUsersRequest{}) - if err != nil { - return diag.Errorf("failed to get list of users: %v", err) - } - - userResource := GetUserDatasource() - for i := range resp.Result { - user := resp.Result[i] - - userdata := userResource.Data(&terraform.InstanceState{}) - userdata.SetId(user.GetId()) - if errDiag := readUser(ctx, userdata, m, clientinfo, org); errDiag != nil { - return errDiag - } - data := resourceToValueMap(userResource, userdata) - users.Add(data) - } - return nil -} - -func readUser(ctx context.Context, d *schema.ResourceData, m interface{}, info *ClientInfo, org string) diag.Diagnostics { - tflog.Info(ctx, "started read") - - client, err := getManagementClient(info, org) - if err != nil { - return diag.FromErr(err) - } - - respUser, err := client.GetUserByID(ctx, &management2.GetUserByIDRequest{Id: d.Id()}) - if err != nil { - return diag.Errorf("failed to get list of users: %v", err) - } - - user := respUser.GetUser() - userType := "" - loginNames := []string{} - for _, v := range user.GetLoginNames() { - loginNames = append(loginNames, v) - } - set := map[string]interface{}{ - idVar: user.GetId(), - resourceOwnerVar: user.GetDetails().GetResourceOwner(), - userStateVar: user.GetState().String(), - userNameVar: user.GetUserName(), - loginNamesVar: loginNames, - preferredLoginNameVar: user.GetPreferredLoginName(), - typeVar: userType, - } - if human := user.GetHuman(); human != nil { - set[typeVar] = v2.HumanUser - if profile := human.GetProfile(); profile != nil { - set[firstNameVar] = profile.GetFirstName() - set[lastNameVar] = profile.GetLastName() - set[displayNameVar] = profile.GetDisplayName() - set[nickNameVar] = profile.GetNickName() - set[preferredLanguageVar] = profile.GetPreferredLanguage() - if gender := profile.GetGender().Number(); gender != 0 { - set[genderVar] = gender - } - } - if email := human.GetEmail(); email != nil { - set[emailVar] = email.GetEmail() - set[isEmailVerifiedVar] = email.GetIsEmailVerified() - } - if phone := human.GetPhone(); phone != nil { - set[phoneVar] = phone.GetPhone() - set[isPhoneVerifiedVar] = phone.GetIsPhoneVerified() - } - } else if machine := user.GetMachine(); machine != nil { - set[typeVar] = v2.MachineUser - set[machineNameVar] = machine.GetName() - set[descriptionVar] = machine.GetDescription() - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of user: %v", k, err) - } - } - d.SetId(user.GetId()) - return nil -} diff --git a/zitadel/v2/action.go b/zitadel/v2/action.go deleted file mode 100644 index fb0e1553..00000000 --- a/zitadel/v2/action.go +++ /dev/null @@ -1,188 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" - "google.golang.org/protobuf/types/known/durationpb" - "time" -) - -const ( - actionOrgId = "org_id" - actionState = "state" - actionName = "name" - actionScript = "script" - actionTimeout = "timeout" - actionAllowedToFail = "allowed_to_fail" -) - -func GetAction() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - actionOrgId: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization", - ForceNew: true, - }, - actionState: { - Type: schema.TypeInt, - Computed: true, - Description: "the state of the action", - }, - actionName: { - Type: schema.TypeString, - Required: true, - Description: "", - }, - actionScript: { - Type: schema.TypeString, - Required: true, - Description: "", - }, - actionTimeout: { - Type: schema.TypeString, - Required: true, - Description: "after which time the action will be terminated if not finished", - }, - actionAllowedToFail: { - Type: schema.TypeBool, - Required: true, - Description: "when true, the next action will be called even if this action fails", - }, - }, - CreateContext: createAction, - DeleteContext: deleteAction, - ReadContext: readAction, - UpdateContext: updateAction, - } -} - -func updateAction(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(actionOrgId).(string)) - if err != nil { - return diag.FromErr(err) - } - - timeout, err := time.ParseDuration(d.Get(actionTimeout).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateAction(ctx, &management2.UpdateActionRequest{ - Id: d.Id(), - Name: d.Get(actionName).(string), - Script: d.Get(actionScript).(string), - Timeout: durationpb.New(timeout), - AllowedToFail: d.Get(actionAllowedToFail).(bool), - }) - if err != nil { - return diag.Errorf("failed to update action: %v", err) - } - return nil -} - -func deleteAction(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started delete") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(actionOrgId).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.DeleteAction(ctx, &management2.DeleteActionRequest{ - Id: d.Id(), - }) - if err != nil { - return diag.Errorf("failed to delete action: %v", err) - } - return nil -} - -func createAction(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(actionOrgId).(string)) - if err != nil { - return diag.FromErr(err) - } - - timeout, err := time.ParseDuration(d.Get(actionTimeout).(string)) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.CreateAction(ctx, &management2.CreateActionRequest{ - Name: d.Get(actionName).(string), - Script: d.Get(actionScript).(string), - Timeout: durationpb.New(timeout), - AllowedToFail: d.Get(actionAllowedToFail).(bool), - }) - if err != nil { - return diag.Errorf("failed to create action: %v", err) - } - d.SetId(resp.GetId()) - return nil -} - -func readAction(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(actionOrgId).(string)) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.ListActions(ctx, &management2.ListActionsRequest{}) - if err != nil { - return diag.Errorf("failed to read action: %v", err) - } - - set := map[string]interface{}{} - actionIDStr := "" - for i := range resp.Result { - action := resp.Result[i] - if action.GetId() == d.Id() { - actionIDStr = d.Id() - set[actionOrgId] = action.GetDetails().GetResourceOwner() - set[actionName] = action.GetName() - set[actionState] = action.GetState() - set[actionScript] = action.GetScript() - set[actionTimeout] = action.GetTimeout().AsDuration().String() - set[actionAllowedToFail] = action.GetAllowedToFail() - } - } - - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of action: %v", k, err) - } - } - d.SetId(actionIDStr) - return nil -} diff --git a/zitadel/v2/application_api.go b/zitadel/v2/application_api.go deleted file mode 100644 index b66e58d3..00000000 --- a/zitadel/v2/application_api.go +++ /dev/null @@ -1,182 +0,0 @@ -package v2 - -import ( - "context" - "fmt" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/zitadel/zitadel-go/v2/pkg/client/management" - "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/app" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" -) - -func GetApplicationAPI() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - applicationOrgIdVar: { - Type: schema.TypeString, - Required: true, - Description: "orgID of the application", - ForceNew: true, - }, - applicationProjectIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the project", - ForceNew: true, - }, - applicationNameVar: { - Type: schema.TypeString, - Required: true, - Description: "Name of the application", - }, - applicationAuthMethodTypeVar: { - Type: schema.TypeString, - Required: true, - Description: "Auth method type", - }, - }, - DeleteContext: deleteApplicationAPI, - CreateContext: createApplicationAPI, - UpdateContext: updateApplicationAPI, - ReadContext: readApplicationAPI, - } -} - -func deleteApplicationAPI(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started delete") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(applicationOrgIdVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.RemoveApp(ctx, &management2.RemoveAppRequest{ - ProjectId: d.Get(applicationProjectIDVar).(string), - AppId: d.Id(), - }) - if err != nil { - return diag.Errorf("failed to delete applicationAPI: %v", err) - } - return nil -} - -func updateApplicationAPI(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(applicationOrgIdVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - projectID := d.Get(applicationProjectIDVar).(string) - appID := d.Id() - apiApp, err := getApp(ctx, client, projectID, appID) - - appName := d.Get(applicationNameVar).(string) - if apiApp.GetName() != appName { - _, err = client.UpdateApp(ctx, &management2.UpdateAppRequest{ - ProjectId: projectID, - AppId: d.Id(), - Name: appName, - }) - if err != nil { - return diag.Errorf("failed to update application: %v", err) - } - } - - apiConfig := apiApp.GetApiConfig() - authMethod := d.Get(applicationAuthMethodTypeVar).(string) - if apiConfig.GetAuthMethodType().String() != authMethod { - _, err = client.UpdateAPIAppConfig(ctx, &management2.UpdateAPIAppConfigRequest{ - ProjectId: d.Get(applicationProjectIDVar).(string), - AppId: d.Id(), - AuthMethodType: app.APIAuthMethodType(app.APIAuthMethodType_value[authMethod]), - }) - if err != nil { - return diag.Errorf("failed to update applicationAPI: %v", err) - } - } - return nil -} - -func createApplicationAPI(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(applicationOrgIdVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.AddAPIApp(ctx, &management2.AddAPIAppRequest{ - ProjectId: d.Get(applicationProjectIDVar).(string), - Name: d.Get(applicationNameVar).(string), - AuthMethodType: app.APIAuthMethodType(app.APIAuthMethodType_value[(d.Get(applicationAuthMethodTypeVar).(string))]), - }) - - if err != nil { - return diag.Errorf("failed to create applicationAPI: %v", err) - } - d.SetId(resp.GetAppId()) - return nil -} - -func readApplicationAPI(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(applicationOrgIdVar).(string)) - if err != nil { - d.SetId("") - return nil - //return diag.FromErr(err) - } - - app, err := getApp(ctx, client, d.Get(applicationProjectIDVar).(string), d.Id()) - if err != nil { - return diag.Errorf("failed to read project: %v", err) - } - - api := app.GetApiConfig() - set := map[string]interface{}{ - applicationOrgIdVar: app.GetDetails().GetResourceOwner(), - applicationNameVar: app.GetName(), - applicationAuthMethodTypeVar: api.GetAuthMethodType().String(), - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of applicationAPI: %v", k, err) - } - } - d.SetId(app.GetId()) - return nil -} - -func getApp(ctx context.Context, client *management.Client, projectID string, appID string) (*app.App, error) { - resp, err := client.GetAppByID(ctx, &management2.GetAppByIDRequest{ProjectId: projectID, AppId: appID}) - if err != nil { - return nil, fmt.Errorf("failed to read project: %v", err) - } - - return resp.GetApp(), err -} diff --git a/zitadel/v2/application_oidc.go b/zitadel/v2/application_oidc.go deleted file mode 100644 index 688258b9..00000000 --- a/zitadel/v2/application_oidc.go +++ /dev/null @@ -1,382 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/app" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" - "google.golang.org/protobuf/types/known/durationpb" - "reflect" - "time" -) - -const ( - applicationOrgIdVar = "org_id" - applicationProjectIDVar = "project_id" - applicationNameVar = "name" - applicationRedirectURIsVar = "redirect_uris" - applicationResponseTypesVar = "response_types" - applicationGrantTypesVar = "grant_types" - applicationAppTypeVar = "app_type" - applicationAuthMethodTypeVar = "auth_method_type" - applicationPostLogoutRedirectURIsVar = "post_logout_redirect_uris" - applicationVersionVar = "version" - applicationDevModeVar = "dev_mode" - applicationAccessTokenTypeVar = "access_token_type" - applicationAccessTokenRoleAssertionVar = "access_token_role_assertion" - applicationIdTokenRoleAssertionVar = "id_token_role_assertion" - applicationIdTokenUserinfoAssertionVar = "id_token_userinfo_assertion" - applicationClockSkewVar = "clock_skew" - applicationAdditionalOriginsVar = "additional_origins" -) - -func GetApplicationOIDC() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - applicationOrgIdVar: { - Type: schema.TypeString, - Required: true, - Description: "orgID of the application", - ForceNew: true, - }, - applicationProjectIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the project", - ForceNew: true, - }, - applicationNameVar: { - Type: schema.TypeString, - Required: true, - Description: "Name of the application", - }, - applicationRedirectURIsVar: { - Type: schema.TypeList, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Required: true, - Description: "RedirectURIs", - }, - applicationResponseTypesVar: { - Type: schema.TypeList, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Required: true, - Description: "Response type", - }, - applicationGrantTypesVar: { - Type: schema.TypeList, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Required: true, - Description: "Grant types", - }, - applicationAppTypeVar: { - Type: schema.TypeString, - Required: true, - Description: "App type", - }, - applicationAuthMethodTypeVar: { - Type: schema.TypeString, - Required: true, - Description: "Auth method type", - }, - applicationPostLogoutRedirectURIsVar: { - Type: schema.TypeList, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Optional: true, - Description: "Post logout redirect URIs", - }, - applicationVersionVar: { - Type: schema.TypeString, - Optional: true, - Description: "Version", - }, - applicationDevModeVar: { - Type: schema.TypeBool, - Optional: true, - Description: "Dev mode", - }, - applicationAccessTokenTypeVar: { - Type: schema.TypeString, - Optional: true, - Description: "Access token type", - }, - applicationAccessTokenRoleAssertionVar: { - Type: schema.TypeBool, - Optional: true, - Description: "Access token role assertion", - }, - applicationIdTokenRoleAssertionVar: { - Type: schema.TypeBool, - Optional: true, - Description: "ID token role assertion", - }, - applicationIdTokenUserinfoAssertionVar: { - Type: schema.TypeBool, - Optional: true, - Description: "Token userinfo assertion", - }, - applicationClockSkewVar: { - Type: schema.TypeString, - Required: true, - Description: "Clockskew", - }, - applicationAdditionalOriginsVar: { - Type: schema.TypeList, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Optional: true, - Description: "Additional origins", - }, - }, - DeleteContext: deleteApplicationOIDC, - CreateContext: createApplicationOIDC, - UpdateContext: updateApplicationOIDC, - ReadContext: readApplicationOIDC, - } -} - -func deleteApplicationOIDC(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started delete") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(applicationOrgIdVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.RemoveApp(ctx, &management2.RemoveAppRequest{ - ProjectId: d.Get(applicationProjectIDVar).(string), - AppId: d.Id(), - }) - if err != nil { - return diag.Errorf("failed to delete applicationOIDC: %v", err) - } - return nil -} - -func updateApplicationOIDC(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(applicationOrgIdVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - respTypes := make([]app.OIDCResponseType, 0) - for _, respType := range d.Get(applicationResponseTypesVar).([]interface{}) { - respTypes = append(respTypes, app.OIDCResponseType(app.OIDCResponseType_value[respType.(string)])) - } - grantTypes := make([]app.OIDCGrantType, 0) - for _, grantType := range d.Get(applicationGrantTypesVar).([]interface{}) { - grantTypes = append(grantTypes, app.OIDCGrantType(app.OIDCGrantType_value[grantType.(string)])) - } - - dur, err := time.ParseDuration(d.Get(applicationClockSkewVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - projectID := d.Get(applicationProjectIDVar).(string) - appID := d.Id() - apiApp, err := getApp(ctx, client, projectID, appID) - - appName := d.Get(applicationNameVar).(string) - if apiApp.GetName() != appName { - _, err = client.UpdateApp(ctx, &management2.UpdateAppRequest{ - ProjectId: projectID, - AppId: appID, - Name: appName, - }) - if err != nil { - return diag.Errorf("failed to update application: %v", err) - } - } - - oidcConfig := apiApp.GetOidcConfig() - redirecURIs := interfaceToStringSlice(d.Get(applicationRedirectURIsVar)) - appType := d.Get(applicationAppTypeVar).(string) - authMethodType := d.Get(applicationAuthMethodTypeVar).(string) - postLogoutRedirectURIs := interfaceToStringSlice(d.Get(applicationPostLogoutRedirectURIsVar)) - devMode := d.Get(applicationDevModeVar).(bool) - accessTokenType := d.Get(applicationAccessTokenTypeVar).(string) - accessTokenRoleAssertion := d.Get(applicationAccessTokenRoleAssertionVar).(bool) - idTokenRoleAssertion := d.Get(applicationIdTokenRoleAssertionVar).(bool) - idTokenUserinfoAssertion := d.Get(applicationIdTokenUserinfoAssertionVar).(bool) - clockSkew := durationpb.New(dur) - additionalOrigins := interfaceToStringSlice(d.Get(applicationAdditionalOriginsVar)) - if !reflect.DeepEqual(redirecURIs, oidcConfig.GetRedirectUris()) || - !reflect.DeepEqual(respTypes, oidcConfig.GetResponseTypes()) || - !reflect.DeepEqual(grantTypes, oidcConfig.GetGrantTypes()) || - appType != oidcConfig.AppType.String() || - authMethodType != oidcConfig.AuthMethodType.String() || - !reflect.DeepEqual(postLogoutRedirectURIs, oidcConfig.GetPostLogoutRedirectUris()) || - devMode != oidcConfig.DevMode || - accessTokenType != oidcConfig.AccessTokenType.String() || - accessTokenRoleAssertion != oidcConfig.AccessTokenRoleAssertion || - clockSkew.String() != oidcConfig.ClockSkew.String() || - !reflect.DeepEqual(additionalOrigins, oidcConfig.GetAdditionalOrigins()) { - _, err = client.UpdateOIDCAppConfig(ctx, &management2.UpdateOIDCAppConfigRequest{ - ProjectId: projectID, - AppId: appID, - RedirectUris: redirecURIs, - ResponseTypes: respTypes, - GrantTypes: grantTypes, - AppType: app.OIDCAppType(app.OIDCAppType_value[appType]), - AuthMethodType: app.OIDCAuthMethodType(app.OIDCAuthMethodType_value[authMethodType]), - PostLogoutRedirectUris: postLogoutRedirectURIs, - DevMode: devMode, - AccessTokenType: app.OIDCTokenType(app.OIDCTokenType_value[accessTokenType]), - AccessTokenRoleAssertion: accessTokenRoleAssertion, - IdTokenRoleAssertion: idTokenRoleAssertion, - IdTokenUserinfoAssertion: idTokenUserinfoAssertion, - ClockSkew: clockSkew, - AdditionalOrigins: additionalOrigins, - }) - if err != nil { - return diag.Errorf("failed to update applicationOIDC: %v", err) - } - } - return nil -} - -func createApplicationOIDC(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(applicationOrgIdVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - respTypes := make([]app.OIDCResponseType, 0) - for _, respType := range d.Get(applicationResponseTypesVar).([]interface{}) { - respTypes = append(respTypes, app.OIDCResponseType(app.OIDCResponseType_value[respType.(string)])) - } - grantTypes := make([]app.OIDCGrantType, 0) - for _, grantType := range d.Get(applicationGrantTypesVar).([]interface{}) { - grantTypes = append(grantTypes, app.OIDCGrantType(app.OIDCGrantType_value[grantType.(string)])) - } - - dur, err := time.ParseDuration(d.Get(applicationClockSkewVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.AddOIDCApp(ctx, &management2.AddOIDCAppRequest{ - ProjectId: d.Get(applicationProjectIDVar).(string), - Name: d.Get(applicationNameVar).(string), - RedirectUris: interfaceToStringSlice(d.Get(applicationRedirectURIsVar)), - ResponseTypes: respTypes, - GrantTypes: grantTypes, - AppType: app.OIDCAppType(app.OIDCAppType_value[(d.Get(applicationAppTypeVar).(string))]), - AuthMethodType: app.OIDCAuthMethodType(app.OIDCAuthMethodType_value[(d.Get(applicationAuthMethodTypeVar).(string))]), - PostLogoutRedirectUris: interfaceToStringSlice(d.Get(applicationPostLogoutRedirectURIsVar)), - DevMode: d.Get(applicationDevModeVar).(bool), - AccessTokenType: app.OIDCTokenType(app.OIDCTokenType_value[(d.Get(applicationAccessTokenTypeVar).(string))]), - AccessTokenRoleAssertion: d.Get(applicationAccessTokenRoleAssertionVar).(bool), - IdTokenRoleAssertion: d.Get(applicationIdTokenRoleAssertionVar).(bool), - IdTokenUserinfoAssertion: d.Get(applicationIdTokenUserinfoAssertionVar).(bool), - ClockSkew: durationpb.New(dur), - AdditionalOrigins: interfaceToStringSlice(d.Get(applicationAdditionalOriginsVar)), - }) - - if err != nil { - return diag.Errorf("failed to create applicationOIDC: %v", err) - } - d.SetId(resp.GetAppId()) - return nil -} - -func readApplicationOIDC(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(applicationOrgIdVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetAppByID(ctx, &management2.GetAppByIDRequest{ProjectId: d.Get(applicationProjectIDVar).(string), AppId: d.Id()}) - if err != nil { - d.SetId("") - return nil - //return diag.Errorf("failed to read application: %v", err) - } - - app := resp.GetApp() - oidc := app.GetOidcConfig() - grantTypes := make([]string, 0) - for _, grantType := range oidc.GetGrantTypes() { - grantTypes = append(grantTypes, grantType.String()) - } - responseTypes := make([]string, 0) - for _, responseType := range oidc.GetResponseTypes() { - responseTypes = append(responseTypes, responseType.String()) - } - clockSkew := oidc.GetClockSkew().String() - if clockSkew == "" { - clockSkew = "0s" - } - - set := map[string]interface{}{ - applicationOrgIdVar: app.GetDetails().GetResourceOwner(), - applicationNameVar: app.GetName(), - applicationRedirectURIsVar: oidc.GetRedirectUris(), - applicationResponseTypesVar: responseTypes, - applicationGrantTypesVar: grantTypes, - applicationAppTypeVar: oidc.GetAppType().String(), - applicationAuthMethodTypeVar: oidc.GetAuthMethodType().String(), - applicationPostLogoutRedirectURIsVar: oidc.GetPostLogoutRedirectUris(), - applicationVersionVar: oidc.GetVersion().String(), - applicationDevModeVar: oidc.GetDevMode(), - applicationAccessTokenTypeVar: oidc.GetAccessTokenType().String(), - applicationAccessTokenRoleAssertionVar: oidc.GetAccessTokenRoleAssertion(), - applicationIdTokenRoleAssertionVar: oidc.GetIdTokenRoleAssertion(), - applicationIdTokenUserinfoAssertionVar: oidc.GetIdTokenUserinfoAssertion(), - applicationClockSkewVar: clockSkew, - applicationAdditionalOriginsVar: oidc.GetAdditionalOrigins(), - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of applicationOIDC: %v", k, err) - } - } - d.SetId(app.GetId()) - return nil -} - -func interfaceToStringSlice(in interface{}) []string { - slice := in.([]interface{}) - ret := make([]string, 0) - for _, item := range slice { - ret = append(ret, item.(string)) - } - return ret -} diff --git a/zitadel/v2/client.go b/zitadel/v2/client.go deleted file mode 100644 index 554410af..00000000 --- a/zitadel/v2/client.go +++ /dev/null @@ -1,89 +0,0 @@ -package v2 - -import ( - "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/zitadel/oidc/pkg/oidc" - "github.com/zitadel/zitadel-go/v2/pkg/client/admin" - "github.com/zitadel/zitadel-go/v2/pkg/client/auth" - "github.com/zitadel/zitadel-go/v2/pkg/client/management" - "github.com/zitadel/zitadel-go/v2/pkg/client/middleware" - "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel" -) - -const ( - IssuerVar = "issuer" - AddressVar = "address" - ProjectVar = "project" - TokenVar = "token" -) - -type ClientInfo struct { - Issuer string - Address string - Project string - Token string -} - -func GetClientInfo(d *schema.ResourceData) (*ClientInfo, error) { - issuer := d.Get(IssuerVar).(string) - address := d.Get(AddressVar).(string) - projectID := d.Get(ProjectVar).(string) - token := d.Get(TokenVar).(string) - - return &ClientInfo{ - issuer, - address, - projectID, - token, - }, nil -} - -func getAuthClient(info *ClientInfo) (*auth.Client, error) { - client, err := auth.NewClient( - info.Issuer, info.Address, - []string{oidc.ScopeOpenID, zitadel.ScopeProjectID(info.Project)}, - zitadel.WithCustomURL(info.Issuer, info.Address), - zitadel.WithJWTProfileTokenSource(middleware.JWTProfileFromPath(info.Token)), - zitadel.WithInsecure(), - ) - if err != nil { - return nil, fmt.Errorf("failed to start zitadel client: %v", err) - } - return client, nil -} - -func getAdminClient(info *ClientInfo) (*admin.Client, error) { - client, err := admin.NewClient( - info.Issuer, info.Address, - []string{oidc.ScopeOpenID, zitadel.ScopeProjectID(info.Project)}, - //zitadel.WithCustomURL(info.Issuer, info.Address), - zitadel.WithJWTProfileTokenSource(middleware.JWTProfileFromPath(info.Token)), - zitadel.WithInsecure(), - ) - if err != nil { - return nil, fmt.Errorf("failed to start zitadel client: %v", err) - } - return client, nil -} - -func getManagementClient(info *ClientInfo, orgID string) (*management.Client, error) { - opts := []zitadel.Option{ - zitadel.WithInsecure(), - //zitadel.WithCustomURL(info.Issuer, info.Address), - zitadel.WithJWTProfileTokenSource(middleware.JWTProfileFromPath(info.Token)), - } - if orgID != "" { - opts = append(opts, zitadel.WithOrgID(orgID)) - } - - client, err := management.NewClient( - info.Issuer, info.Address, - []string{oidc.ScopeOpenID, zitadel.ScopeProjectID(info.Project)}, - opts..., - ) - if err != nil { - return nil, fmt.Errorf("failed to start zitadel client: %v", err) - } - return client, nil -} diff --git a/zitadel/v2/domain.go b/zitadel/v2/domain.go deleted file mode 100644 index a460a77c..00000000 --- a/zitadel/v2/domain.go +++ /dev/null @@ -1,152 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" - "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/object" - "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/org" -) - -const ( - domainOrgIdVar = "org_id" - domainNameVar = "name" - domainIsVerified = "is_verified" - domainIsPrimary = "is_primary" - domainValidationType = "validation_type" -) - -func GetDomain() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - domainNameVar: { - Type: schema.TypeString, - Required: true, - Description: "Name of the domain", - ForceNew: true, - }, - domainOrgIdVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization", - ForceNew: true, - }, - domainIsVerified: { - Type: schema.TypeBool, - Computed: true, - Description: "Is domain verified", - }, - domainIsPrimary: { - Type: schema.TypeBool, - Computed: true, - Description: "Is domain primary", - }, - domainValidationType: { - Type: schema.TypeInt, - Computed: true, - Description: "Validation type", - }, - }, - ReadContext: readDomain, - CreateContext: createDomain, - DeleteContext: deleteDomain, - } -} - -func deleteDomain(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started delete") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(domainOrgIdVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.RemoveOrgDomain(ctx, &management2.RemoveOrgDomainRequest{ - Domain: d.Id(), - }) - if err != nil { - return diag.Errorf("failed to delete domain: %v", err) - } - return nil -} - -func createDomain(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(domainOrgIdVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - name := d.Get(domainNameVar).(string) - _, err = client.AddOrgDomain(ctx, &management2.AddOrgDomainRequest{ - Domain: name, - }) - if err != nil { - return diag.Errorf("failed to create domain: %v", err) - } - d.SetId(name) - return nil -} - -func readDomain(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(domainOrgIdVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.ListOrgDomains(ctx, &management2.ListOrgDomainsRequest{ - Queries: []*org.DomainSearchQuery{ - {Query: &org.DomainSearchQuery_DomainNameQuery{ - DomainNameQuery: &org.DomainNameQuery{ - Name: d.Id(), - Method: object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS, - }, - }, - }, - }, - }) - if err != nil { - return diag.Errorf("failed to read domain: %v", err) - } - - if len(resp.Result) == 1 { - domain := resp.Result[0] - set := map[string]interface{}{ - domainNameVar: domain.GetDomainName(), - domainOrgIdVar: domain.GetOrgId(), - domainIsVerified: domain.GetIsVerified(), - domainIsPrimary: domain.GetIsPrimary(), - domainValidationType: domain.GetValidationType().Number(), - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of domain: %v", k, err) - } - } - d.SetId(domain.GetDomainName()) - return nil - } - - d.SetId("") - return nil -} diff --git a/zitadel/v2/domain_policy.go b/zitadel/v2/domain_policy.go deleted file mode 100644 index bf9f18d8..00000000 --- a/zitadel/v2/domain_policy.go +++ /dev/null @@ -1,169 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - admin2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" -) - -const ( - domainPolicyOrgIdVar = "org_id" - domainPolicyUserLoginMustBeDomain = "user_login_must_be_domain" - domainPolicyIsDefault = "is_default" - domainPolicyValidateOrgDomain = "validate_org_domains" - domainPolicySmtpSender = "smtp_sender_address_matches_instance_domain" -) - -func GetDomainPolicy() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - domainPolicyOrgIdVar: { - Type: schema.TypeString, - Required: true, - Description: "Id for the organization", - ForceNew: true, - }, - domainPolicyUserLoginMustBeDomain: { - Type: schema.TypeBool, - Required: true, - Description: "User login must be domain", - }, - domainPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "Is this policy the default", - }, - domainPolicyValidateOrgDomain: { - Type: schema.TypeBool, - Required: true, - Description: "Validate organization domains", - }, - domainPolicySmtpSender: { - Type: schema.TypeBool, - Required: true, - Description: "", - }, - }, - ReadContext: readDomainPolicy, - CreateContext: createDomainPolicy, - DeleteContext: deleteDomainPolicy, - UpdateContext: updateDomainPolicy, - } -} - -func deleteDomainPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getAdminClient(clientinfo) - if err != nil { - return diag.FromErr(err) - } - org := d.Get(domainPolicyOrgIdVar).(string) - - _, err = client.ResetCustomDomainPolicyToDefault(ctx, &admin2.ResetCustomDomainPolicyToDefaultRequest{ - OrgId: org, - }) - if err != nil { - return diag.Errorf("failed to reset domain policy: %v", err) - } - d.SetId(org) - return nil -} - -func updateDomainPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getAdminClient(clientinfo) - if err != nil { - return diag.FromErr(err) - } - org := d.Get(domainPolicyOrgIdVar).(string) - - _, err = client.UpdateCustomDomainPolicy(ctx, &admin2.UpdateCustomDomainPolicyRequest{ - OrgId: org, - UserLoginMustBeDomain: d.Get(domainPolicyUserLoginMustBeDomain).(bool), - ValidateOrgDomains: d.Get(domainPolicyValidateOrgDomain).(bool), - SmtpSenderAddressMatchesInstanceDomain: d.Get(domainPolicySmtpSender).(bool), - }) - if err != nil { - return diag.Errorf("failed to update domain policy: %v", err) - } - d.SetId(org) - return nil -} - -func createDomainPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getAdminClient(clientinfo) - if err != nil { - return diag.FromErr(err) - } - org := d.Get(domainPolicyOrgIdVar).(string) - - _, err = client.AddCustomDomainPolicy(ctx, &admin2.AddCustomDomainPolicyRequest{ - OrgId: org, - UserLoginMustBeDomain: d.Get(domainPolicyUserLoginMustBeDomain).(bool), - ValidateOrgDomains: d.Get(domainPolicyValidateOrgDomain).(bool), - SmtpSenderAddressMatchesInstanceDomain: d.Get(domainPolicySmtpSender).(bool), - }) - if err != nil { - return diag.Errorf("failed to create domain policy: %v", err) - } - d.SetId(org) - return nil -} - -func readDomainPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(domainPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetDomainPolicy(ctx, &management2.GetDomainPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get domain policy: %v", err) - } - - policy := resp.Policy - set := map[string]interface{}{ - domainPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - domainPolicyIsDefault: policy.GetIsDefault(), - domainPolicyUserLoginMustBeDomain: policy.GetUserLoginMustBeDomain(), - domainPolicyValidateOrgDomain: policy.GetValidateOrgDomains(), - } - - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of domain: %v", k, err) - } - } - d.SetId(policy.GetDetails().GetResourceOwner()) - return nil -} diff --git a/zitadel/v2/label_policy.go b/zitadel/v2/label_policy.go deleted file mode 100644 index 4c6ef10f..00000000 --- a/zitadel/v2/label_policy.go +++ /dev/null @@ -1,263 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" -) - -const ( - labelPolicyOrgIdVar = "org_id" - labelPolicyPrimaryColor = "primary_color" - labelPolicyIsDefault = "is_default" - labelPolicyHideLoginNameSuffix = "hide_login_name_suffix" - labelPolicyWarnColor = "warn_color" - labelPolicyBackgroundColor = "background_color" - labelPolicyFontColor = "font_color" - labelPolicyPrimaryColorDark = "primary_color_dark" - labelPolicyBackgroundColorDark = "background_color_dark" - labelPolicyWarnColorDark = "warn_color_dark" - labelPolicyFontColorDark = "font_color_dark" - labelPolicyDisableWatermark = "disable_watermark" - labelPolicyLogoURL = "logo_url" - labelPolicyIconURL = "icon_url" - labelPolicyLogoURLDark = "logo_url_dark" - labelPolicyIconURLDark = "icon_url_dark" - labelPolicyFontURL = "font_url" -) - -func GetLabelPolicy() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - labelPolicyOrgIdVar: { - Type: schema.TypeString, - Required: true, - Description: "Id for the organization", - ForceNew: true, - }, - labelPolicyPrimaryColor: { - Type: schema.TypeString, - Required: true, - Description: "hex value for primary color", - }, - labelPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the organisation's admin changed the policy", - }, - labelPolicyHideLoginNameSuffix: { - Type: schema.TypeBool, - Required: true, - Description: "hides the org suffix on the login form if the scope \"urn:zitadel:iam:org:domain:primary:{domainname}\" is set. Details about this scope in https://docs.zitadel.ch/concepts#Reserved_Scopes", - }, - labelPolicyWarnColor: { - Type: schema.TypeString, - Required: true, - Description: "hex value for warn color", - }, - labelPolicyBackgroundColor: { - Type: schema.TypeString, - Required: true, - Description: "hex value for background color", - }, - labelPolicyFontColor: { - Type: schema.TypeString, - Required: true, - Description: "hex value for font color", - }, - labelPolicyPrimaryColorDark: { - Type: schema.TypeString, - Required: true, - Description: "hex value for primary color dark theme", - }, - labelPolicyBackgroundColorDark: { - Type: schema.TypeString, - Required: true, - Description: "hex value for background color dark theme", - }, - labelPolicyWarnColorDark: { - Type: schema.TypeString, - Required: true, - Description: "hex value for warn color dark theme", - }, - labelPolicyFontColorDark: { - Type: schema.TypeString, - Required: true, - Description: "hex value for font color dark theme", - }, - labelPolicyDisableWatermark: { - Type: schema.TypeBool, - Required: true, - Description: "", - }, - labelPolicyLogoURL: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - labelPolicyIconURL: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - labelPolicyLogoURLDark: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - labelPolicyIconURLDark: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - labelPolicyFontURL: { - Type: schema.TypeString, - Computed: true, - Description: "", - }, - }, - ReadContext: readLabelPolicy, - CreateContext: createLabelPolicy, - DeleteContext: deleteLabelPolicy, - UpdateContext: updateLabelPolicy, - } -} - -func deleteLabelPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(labelPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.ResetLabelPolicyToDefault(ctx, &management2.ResetLabelPolicyToDefaultRequest{}) - if err != nil { - return diag.Errorf("failed to reset label policy: %v", err) - } - d.SetId(org) - return nil -} - -func updateLabelPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(labelPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateCustomLabelPolicy(ctx, &management2.UpdateCustomLabelPolicyRequest{ - PrimaryColor: d.Get(labelPolicyPrimaryColor).(string), - HideLoginNameSuffix: d.Get(labelPolicyHideLoginNameSuffix).(bool), - WarnColor: d.Get(labelPolicyWarnColor).(string), - BackgroundColor: d.Get(labelPolicyBackgroundColor).(string), - FontColor: d.Get(labelPolicyFontColor).(string), - PrimaryColorDark: d.Get(labelPolicyPrimaryColorDark).(string), - BackgroundColorDark: d.Get(labelPolicyBackgroundColorDark).(string), - WarnColorDark: d.Get(labelPolicyWarnColorDark).(string), - FontColorDark: d.Get(labelPolicyFontColorDark).(string), - DisableWatermark: d.Get(labelPolicyDisableWatermark).(bool), - }) - if err != nil { - return diag.Errorf("failed to update label policy: %v", err) - } - d.SetId(org) - return nil -} - -func createLabelPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(labelPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.AddCustomLabelPolicy(ctx, &management2.AddCustomLabelPolicyRequest{ - PrimaryColor: d.Get(labelPolicyPrimaryColor).(string), - HideLoginNameSuffix: d.Get(labelPolicyHideLoginNameSuffix).(bool), - WarnColor: d.Get(labelPolicyWarnColor).(string), - BackgroundColor: d.Get(labelPolicyBackgroundColor).(string), - FontColor: d.Get(labelPolicyFontColor).(string), - PrimaryColorDark: d.Get(labelPolicyPrimaryColorDark).(string), - BackgroundColorDark: d.Get(labelPolicyBackgroundColorDark).(string), - WarnColorDark: d.Get(labelPolicyWarnColorDark).(string), - FontColorDark: d.Get(labelPolicyFontColorDark).(string), - DisableWatermark: d.Get(labelPolicyDisableWatermark).(bool), - }) - if err != nil { - return diag.Errorf("failed to create label policy: %v", err) - } - d.SetId(org) - return nil -} - -func readLabelPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(domainPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetLabelPolicy(ctx, &management2.GetLabelPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get domain policy: %v", err) - } - - policy := resp.Policy - set := map[string]interface{}{ - labelPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - labelPolicyIsDefault: policy.GetIsDefault(), - labelPolicyPrimaryColor: policy.GetPrimaryColor(), - labelPolicyHideLoginNameSuffix: policy.GetHideLoginNameSuffix(), - labelPolicyWarnColor: policy.GetWarnColor(), - labelPolicyBackgroundColor: policy.GetBackgroundColor(), - labelPolicyFontColor: policy.GetFontColor(), - labelPolicyPrimaryColorDark: policy.GetPrimaryColorDark(), - labelPolicyBackgroundColorDark: policy.GetBackgroundColorDark(), - labelPolicyWarnColorDark: policy.GetWarnColorDark(), - labelPolicyFontColorDark: policy.GetFontColorDark(), - labelPolicyDisableWatermark: policy.GetDisableWatermark(), - labelPolicyLogoURL: policy.GetLogoUrl(), - labelPolicyIconURL: policy.GetIconUrl(), - labelPolicyLogoURLDark: policy.GetLogoUrlDark(), - labelPolicyIconURLDark: policy.GetIconUrlDark(), - labelPolicyFontURL: policy.GetFontUrl(), - } - - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of label policy: %v", k, err) - } - } - d.SetId(policy.GetDetails().GetResourceOwner()) - return nil -} diff --git a/zitadel/v2/lockout_policy.go b/zitadel/v2/lockout_policy.go deleted file mode 100644 index dc3503f1..00000000 --- a/zitadel/v2/lockout_policy.go +++ /dev/null @@ -1,148 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" -) - -const ( - lockoutPolicyOrgIdVar = "org_id" - lockoutPolicyMaxPasswordAttempts = "max_password_attempts" - lockoutPolicyIsDefault = "is_default" -) - -func GetLockoutPolicy() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - lockoutPolicyOrgIdVar: { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Id for the organization", - ForceNew: true, - }, - lockoutPolicyMaxPasswordAttempts: { - Type: schema.TypeInt, - Required: true, - Description: "Maximum password check attempts before the account gets locked. Attempts are reset as soon as the password is entered correct or the password is reset.", - }, - lockoutPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the organisation's admin changed the policy", - }, - }, - DeleteContext: deleteLockoutPolicy, - CreateContext: createLockoutPolicy, - UpdateContext: updateLockoutPolicy, - ReadContext: readLockoutPolicy, - } -} - -func deleteLockoutPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(lockoutPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.ResetLockoutPolicyToDefault(ctx, &management2.ResetLockoutPolicyToDefaultRequest{}) - if err != nil { - return diag.Errorf("failed to reset lockout policy: %v", err) - } - d.SetId(org) - return nil -} - -func updateLockoutPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(lockoutPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateCustomLockoutPolicy(ctx, &management2.UpdateCustomLockoutPolicyRequest{ - MaxPasswordAttempts: d.Get(lockoutPolicyMaxPasswordAttempts).(uint32), - }) - if err != nil { - return diag.Errorf("failed to update lockout policy: %v", err) - } - d.SetId(org) - return nil -} - -func createLockoutPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(lockoutPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.AddCustomLockoutPolicy(ctx, &management2.AddCustomLockoutPolicyRequest{ - MaxPasswordAttempts: d.Get(lockoutPolicyMaxPasswordAttempts).(uint32), - }) - if err != nil { - return diag.Errorf("failed to create lockout policy: %v", err) - } - d.SetId(org) - return nil -} - -func readLockoutPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(domainPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetLockoutPolicy(ctx, &management2.GetLockoutPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get lockout policy: %v", err) - } - - policy := resp.Policy - set := map[string]interface{}{ - lockoutPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - lockoutPolicyIsDefault: policy.GetIsDefault(), - lockoutPolicyMaxPasswordAttempts: policy.GetMaxPasswordAttempts(), - } - - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of lockout policy: %v", k, err) - } - } - d.SetId(policy.GetDetails().GetResourceOwner()) - return nil -} diff --git a/zitadel/v2/login_policy.go b/zitadel/v2/login_policy.go deleted file mode 100644 index 6d46df9a..00000000 --- a/zitadel/v2/login_policy.go +++ /dev/null @@ -1,298 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" - "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/policy" - "google.golang.org/protobuf/types/known/durationpb" - "time" -) - -const ( - loginPolicyOrgIdVar = "org_id" - loginPolicyAllowUsernamePassword = "user_login" - loginPolicyAllowRegister = "allow_register" - loginPolicyAllowExternalIDP = "allow_external_idp" - loginPolicyForceMFA = "force_mfa" - loginPolicyPasswordlessType = "passwordless_type" - loginPolicyIsDefault = "is_default" - loginPolicyHidePasswordReset = "hide_password_reset" - loginPolicyPasswordCheckLifetime = "password_check_lifetime" - loginPolicyExternalLoginCheckLifetime = "external_login_check_lifetime" - loginPolicyMFAInitSkipLifetime = "mfa_init_skip_lifetime" - loginPolicySecondFactorCheckLifetime = "second_factor_check_lifetime" - loginPolicyMultiFactorCheckLifetime = "multi_factor_check_lifetime" - loginPolicyIgnoreUnknownUsernames = "ignore_unknown_usernames" - loginPolicyDefaultRedirectURI = "default_redirect_uri" -) - -func GetLoginPolicy() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - loginPolicyOrgIdVar: { - Type: schema.TypeString, - Required: true, - Description: "Id for the organization", - ForceNew: true, - }, - loginPolicyAllowUsernamePassword: { - Type: schema.TypeBool, - Required: true, - Description: "defines if a user is allowed to login with his username and password", - }, - loginPolicyAllowRegister: { - Type: schema.TypeBool, - Required: true, - Description: "defines if a person is allowed to register a user on this organisation", - }, - loginPolicyAllowExternalIDP: { - Type: schema.TypeBool, - Required: true, - Description: "defines if a user is allowed to add a defined identity provider. E.g. Google auth", - }, - loginPolicyForceMFA: { - Type: schema.TypeBool, - Required: true, - Description: "defines if a user MUST use a multi factor to log in", - }, - loginPolicyPasswordlessType: { - Type: schema.TypeInt, - Required: true, - Description: "defines if passwordless is allowed for users", - }, - loginPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the organisation's admin changed the policy", - }, - loginPolicyHidePasswordReset: { - Type: schema.TypeBool, - Required: true, - Description: "defines if password reset link should be shown in the login screen", - }, - loginPolicyIgnoreUnknownUsernames: { - Type: schema.TypeBool, - Required: true, - Description: "defines if unknown username on login screen directly return an error or always display the password screen", - }, - loginPolicyDefaultRedirectURI: { - Type: schema.TypeString, - Required: true, - Description: "defines where the user will be redirected to if the login is started without app context (e.g. from mail)", - }, - loginPolicyPasswordCheckLifetime: { - Type: schema.TypeString, - Required: true, - Description: "", - }, - loginPolicyExternalLoginCheckLifetime: { - Type: schema.TypeString, - Required: true, - Description: "", - }, - loginPolicyMFAInitSkipLifetime: { - Type: schema.TypeString, - Required: true, - Description: "", - }, - loginPolicySecondFactorCheckLifetime: { - Type: schema.TypeString, - Required: true, - Description: "", - }, - loginPolicyMultiFactorCheckLifetime: { - Type: schema.TypeString, - Required: true, - Description: "", - }, - }, - CreateContext: createLoginPolicy, - UpdateContext: updateLoginPolicy, - DeleteContext: deleteLoginPolicy, - ReadContext: readLoginPolicy, - } -} - -func deleteLoginPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(loginPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.ResetLoginPolicyToDefault(ctx, &management2.ResetLoginPolicyToDefaultRequest{}) - if err != nil { - return diag.Errorf("failed to reset login policy: %v", err) - } - d.SetId(org) - return nil -} - -func updateLoginPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(loginPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - passwordCheckLT, err := time.ParseDuration(d.Get(loginPolicyPasswordCheckLifetime).(string)) - if err != nil { - return diag.FromErr(err) - } - externalLoginCheckLT, err := time.ParseDuration(d.Get(loginPolicyExternalLoginCheckLifetime).(string)) - if err != nil { - return diag.FromErr(err) - } - mfaInitSkipLT, err := time.ParseDuration(d.Get(loginPolicyMFAInitSkipLifetime).(string)) - if err != nil { - return diag.FromErr(err) - } - secondFactorCheckLT, err := time.ParseDuration(d.Get(loginPolicySecondFactorCheckLifetime).(string)) - if err != nil { - return diag.FromErr(err) - } - multiFactorCheckLT, err := time.ParseDuration(d.Get(loginPolicyMultiFactorCheckLifetime).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateCustomLoginPolicy(ctx, &management2.UpdateCustomLoginPolicyRequest{ - AllowUsernamePassword: d.Get(loginPolicyAllowUsernamePassword).(bool), - AllowRegister: d.Get(loginPolicyAllowRegister).(bool), - AllowExternalIdp: d.Get(loginPolicyAllowExternalIDP).(bool), - ForceMfa: d.Get(loginPolicyForceMFA).(bool), - PasswordlessType: d.Get(loginPolicyPasswordlessType).(policy.PasswordlessType), - HidePasswordReset: d.Get(loginPolicyHidePasswordReset).(bool), - IgnoreUnknownUsernames: d.Get(loginPolicyIgnoreUnknownUsernames).(bool), - DefaultRedirectUri: d.Get(loginPolicyDefaultRedirectURI).(string), - PasswordCheckLifetime: durationpb.New(passwordCheckLT), - ExternalLoginCheckLifetime: durationpb.New(externalLoginCheckLT), - MfaInitSkipLifetime: durationpb.New(mfaInitSkipLT), - SecondFactorCheckLifetime: durationpb.New(secondFactorCheckLT), - MultiFactorCheckLifetime: durationpb.New(multiFactorCheckLT), - }) - if err != nil { - return diag.Errorf("failed to update login policy: %v", err) - } - d.SetId(org) - return nil -} - -func createLoginPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(loginPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - passwordCheckLT, err := time.ParseDuration(d.Get(loginPolicyPasswordCheckLifetime).(string)) - if err != nil { - return diag.FromErr(err) - } - externalLoginCheckLT, err := time.ParseDuration(d.Get(loginPolicyExternalLoginCheckLifetime).(string)) - if err != nil { - return diag.FromErr(err) - } - mfaInitSkipLT, err := time.ParseDuration(d.Get(loginPolicyMFAInitSkipLifetime).(string)) - if err != nil { - return diag.FromErr(err) - } - secondFactorCheckLT, err := time.ParseDuration(d.Get(loginPolicySecondFactorCheckLifetime).(string)) - if err != nil { - return diag.FromErr(err) - } - multiFactorCheckLT, err := time.ParseDuration(d.Get(loginPolicyMultiFactorCheckLifetime).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.AddCustomLoginPolicy(ctx, &management2.AddCustomLoginPolicyRequest{ - AllowUsernamePassword: d.Get(loginPolicyAllowUsernamePassword).(bool), - AllowRegister: d.Get(loginPolicyAllowRegister).(bool), - AllowExternalIdp: d.Get(loginPolicyAllowExternalIDP).(bool), - ForceMfa: d.Get(loginPolicyForceMFA).(bool), - PasswordlessType: d.Get(loginPolicyPasswordlessType).(policy.PasswordlessType), - HidePasswordReset: d.Get(loginPolicyHidePasswordReset).(bool), - IgnoreUnknownUsernames: d.Get(loginPolicyIgnoreUnknownUsernames).(bool), - DefaultRedirectUri: d.Get(loginPolicyDefaultRedirectURI).(string), - PasswordCheckLifetime: durationpb.New(passwordCheckLT), - ExternalLoginCheckLifetime: durationpb.New(externalLoginCheckLT), - MfaInitSkipLifetime: durationpb.New(mfaInitSkipLT), - SecondFactorCheckLifetime: durationpb.New(secondFactorCheckLT), - MultiFactorCheckLifetime: durationpb.New(multiFactorCheckLT), - }) - if err != nil { - return diag.Errorf("failed to create login policy: %v", err) - } - d.SetId(org) - return nil -} - -func readLoginPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(domainPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetLoginPolicy(ctx, &management2.GetLoginPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get login policy: %v", err) - } - - policy := resp.Policy - set := map[string]interface{}{ - loginPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - loginPolicyIsDefault: policy.GetIsDefault(), - loginPolicyAllowUsernamePassword: policy.GetAllowUsernamePassword(), - loginPolicyAllowRegister: policy.GetAllowRegister(), - loginPolicyAllowExternalIDP: policy.GetAllowExternalIdp(), - loginPolicyForceMFA: policy.GetForceMfa(), - loginPolicyPasswordlessType: policy.GetPasswordlessType(), - loginPolicyHidePasswordReset: policy.GetHidePasswordReset(), - loginPolicyPasswordCheckLifetime: policy.GetPasswordCheckLifetime(), - loginPolicyExternalLoginCheckLifetime: policy.GetExternalLoginCheckLifetime(), - loginPolicyMFAInitSkipLifetime: policy.GetMfaInitSkipLifetime(), - loginPolicySecondFactorCheckLifetime: policy.GetSecondFactorCheckLifetime(), - loginPolicyMultiFactorCheckLifetime: policy.GetMultiFactorCheckLifetime(), - } - - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of login policy: %v", k, err) - } - } - d.SetId(policy.GetDetails().GetResourceOwner()) - return nil -} diff --git a/zitadel/v2/org.go b/zitadel/v2/org.go deleted file mode 100644 index 480b38b4..00000000 --- a/zitadel/v2/org.go +++ /dev/null @@ -1,119 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - admin2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" - "strings" -) - -const ( - nameVar = "name" -) - -func OrgResource() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - nameVar: { - Type: schema.TypeString, - Required: true, - Description: "Name of the org", - ForceNew: true, - }, - }, - CreateContext: createOrg, - DeleteContext: deleteOrg, - ReadContext: readOrg, - } -} - -func deleteOrg(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - /*client, ok := m.(*management.Client) - if !ok { - return diag.Errorf("failed to get client") - } - - _, err := client.DeactivateOrg(ctx, &management2.DeactivateOrgRequest{}) - if err != nil { - return diag.FromErr(err) - }*/ - return nil -} - -func createOrg(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - client, err := getManagementClient(clientinfo, "") - if err != nil { - return diag.FromErr(err) - } - - if errDiag := readOrg(ctx, d, m); errDiag != nil { - return errDiag - } - - if d.Id() == "" { - resp, err := client.AddOrg(ctx, &management2.AddOrgRequest{ - Name: d.Get(nameVar).(string), - }) - if err != nil { - return diag.FromErr(err) - } - d.SetId(resp.GetId()) - } - - return nil -} - -func readOrg(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - client, err := getAdminClient(clientinfo) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.ListOrgs(ctx, &admin2.ListOrgsRequest{}) - if err != nil { - return diag.FromErr(err) - } - tflog.Debug(ctx, "found orgs", map[string]interface{}{ - "orglist": resp.Result, - }) - - //id := d.Get("id").(string) - name := d.Get(nameVar).(string) - tflog.Debug(ctx, "check if org is existing", map[string]interface{}{ - // "id": id, - "org": name, - }) - - for i := range resp.Result { - org := resp.Result[i] - - if strings.Compare(org.GetName(), name) == 0 { - d.SetId(org.GetId()) - - tflog.Debug(ctx, "found org", map[string]interface{}{ - "id": d.Get("id"), - "org": name, - }) - return nil - } - } - - d.SetId("") - tflog.Debug(ctx, "org not found", map[string]interface{}{ - "org": name, - }) - return nil -} diff --git a/zitadel/v2/org_member.go b/zitadel/v2/org_member.go deleted file mode 100644 index 87d75e06..00000000 --- a/zitadel/v2/org_member.go +++ /dev/null @@ -1,170 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" - "strings" -) - -const ( - orgMemberOrgIDVar = "org_id" - orgMemberUserIDVar = "user_id" - orgMemberRolesVar = "roles" -) - -func GetOrgMember() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - orgMemberOrgIDVar: { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "ID of the organization", - }, - orgMemberUserIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the user", - ForceNew: true, - }, - orgMemberRolesVar: { - Type: schema.TypeSet, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Required: true, - Description: "List of roles granted", - }, - }, - DeleteContext: deleteOrgMember, - CreateContext: createOrgMember, - UpdateContext: updateOrgMember, - ReadContext: readOrgMember, - } -} - -func deleteOrgMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started delete") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(orgMemberOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.RemoveOrgMember(ctx, &management2.RemoveOrgMemberRequest{ - UserId: d.Get(orgMemberUserIDVar).(string), - }) - if err != nil { - return diag.Errorf("failed to delete orgmember: %v", err) - } - return nil -} - -func updateOrgMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(orgMemberOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateOrgMember(ctx, &management2.UpdateOrgMemberRequest{ - UserId: d.Get(orgMemberUserIDVar).(string), - Roles: d.Get(orgMemberRolesVar).([]string), - }) - if err != nil { - return diag.Errorf("failed to update orgmember: %v", err) - } - return nil -} - -func createOrgMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(orgMemberOrgIDVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - userID := d.Get(orgMemberUserIDVar).(string) - roles := make([]string, 0) - for _, role := range d.Get(orgMemberRolesVar).(*schema.Set).List() { - roles = append(roles, role.(string)) - } - - _, err = client.AddOrgMember(ctx, &management2.AddOrgMemberRequest{ - UserId: userID, - Roles: roles, - }) - if err != nil { - return diag.Errorf("failed to create orgmember: %v", err) - } - d.SetId(getOrgMemberID(org, userID)) - return nil -} - -func readOrgMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - org := d.Get(orgMemberOrgIDVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.ListOrgMembers(ctx, &management2.ListOrgMembersRequest{}) - if err != nil { - return diag.Errorf("failed to read orgmember: %v", err) - } - - userID := d.Get(orgMemberUserIDVar).(string) - for _, orgMember := range resp.Result { - if orgMember.UserId == userID { - set := map[string]interface{}{ - orgMemberUserIDVar: orgMember.GetUserId(), - orgMemberOrgIDVar: orgMember.GetDetails().GetResourceOwner(), - orgMemberRolesVar: orgMember.GetRoles(), - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of orgmember: %v", k, err) - } - } - d.SetId(getOrgMemberID(org, userID)) - return nil - } - } - return nil -} - -func getOrgMemberID(org string, userID string) string { - return org + "_" + userID -} - -func splitOrgMemberID(orgMemberID string) (string, string) { - parts := strings.Split(orgMemberID, "_") - return parts[0], parts[1] -} diff --git a/zitadel/v2/password_complexity_policy.go b/zitadel/v2/password_complexity_policy.go deleted file mode 100644 index 7c85726e..00000000 --- a/zitadel/v2/password_complexity_policy.go +++ /dev/null @@ -1,183 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" -) - -const ( - passwordCompPolicyOrgIdVar = "org_id" - passwordCompPolicyMinLength = "min_length" - passwordCompPolicyHasUppercase = "has_uppercase" - passwordCompPolicyHasLowercase = "has_lowercase" - passwordCompPolicyHasNumber = "has_number" - passwordCompPolicyHasSymbol = "has_symbol" - passwordCompPolicyIsDefault = "is_default" -) - -func GetPasswordComplexityPolicy() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - passwordCompPolicyOrgIdVar: { - Type: schema.TypeString, - Required: true, - Description: "Id for the organization", - ForceNew: true, - }, - passwordCompPolicyMinLength: { - Type: schema.TypeInt, - Required: true, - Description: "Minimal length for the password", - }, - passwordCompPolicyHasUppercase: { - Type: schema.TypeBool, - Required: true, - Description: "defines if the password MUST contain an upper case letter", - }, - passwordCompPolicyHasLowercase: { - Type: schema.TypeBool, - Required: true, - Description: "defines if the password MUST contain a lower case letter", - }, - passwordCompPolicyHasNumber: { - Type: schema.TypeBool, - Required: true, - Description: "defines if the password MUST contain a number", - }, - passwordCompPolicyHasSymbol: { - Type: schema.TypeBool, - Required: true, - Description: "defines if the password MUST contain a symbol. E.g. \"$\"", - }, - passwordCompPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "defines if the organisation's admin changed the policy", - }, - }, - DeleteContext: deletePasswordComplexityPolicy, - ReadContext: readPasswordComplexityPolicy, - CreateContext: createPasswordComplexityPolicy, - UpdateContext: updatePasswordComplexityPolicy, - } -} - -func deletePasswordComplexityPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(passwordCompPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.ResetPasswordComplexityPolicyToDefault(ctx, &management2.ResetPasswordComplexityPolicyToDefaultRequest{}) - if err != nil { - return diag.Errorf("failed to reset password complexity policy: %v", err) - } - d.SetId(org) - return nil -} - -func updatePasswordComplexityPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(passwordCompPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateCustomPasswordComplexityPolicy(ctx, &management2.UpdateCustomPasswordComplexityPolicyRequest{ - MinLength: d.Get(passwordCompPolicyMinLength).(uint64), - HasUppercase: d.Get(passwordCompPolicyHasUppercase).(bool), - HasLowercase: d.Get(passwordCompPolicyHasLowercase).(bool), - HasNumber: d.Get(passwordCompPolicyHasNumber).(bool), - HasSymbol: d.Get(passwordCompPolicyHasSymbol).(bool), - }) - if err != nil { - return diag.Errorf("failed to update password complexity policy: %v", err) - } - d.SetId(org) - return nil -} - -func createPasswordComplexityPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(passwordCompPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.AddCustomPasswordComplexityPolicy(ctx, &management2.AddCustomPasswordComplexityPolicyRequest{ - MinLength: d.Get(passwordCompPolicyMinLength).(uint64), - HasUppercase: d.Get(passwordCompPolicyHasUppercase).(bool), - HasLowercase: d.Get(passwordCompPolicyHasLowercase).(bool), - HasNumber: d.Get(passwordCompPolicyHasNumber).(bool), - HasSymbol: d.Get(passwordCompPolicyHasSymbol).(bool), - }) - if err != nil { - return diag.Errorf("failed to create password complexity policy: %v", err) - } - d.SetId(org) - return nil -} - -func readPasswordComplexityPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(domainPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetPasswordComplexityPolicy(ctx, &management2.GetPasswordComplexityPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get password complexity policy: %v", err) - } - - policy := resp.Policy - set := map[string]interface{}{ - passwordCompPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - passwordCompPolicyIsDefault: policy.GetIsDefault(), - passwordCompPolicyMinLength: policy.GetMinLength(), - passwordCompPolicyHasUppercase: policy.GetHasUppercase(), - passwordCompPolicyHasLowercase: policy.GetHasLowercase(), - passwordCompPolicyHasNumber: policy.GetHasNumber(), - passwordCompPolicyHasSymbol: policy.GetHasSymbol(), - } - - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of password complexity policy: %v", k, err) - } - } - d.SetId(policy.GetDetails().GetResourceOwner()) - return nil -} diff --git a/zitadel/v2/privacy_policy.go b/zitadel/v2/privacy_policy.go deleted file mode 100644 index 67e17f44..00000000 --- a/zitadel/v2/privacy_policy.go +++ /dev/null @@ -1,165 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" -) - -const ( - privacyPolicyOrgIdVar = "org_id" - privacyPolicyTOSLink = "tos_link" - privacyPolicyPrivacyLink = "privacy_link" - privacyPolicyIsDefault = "is_default" - privacyPolicyHelpLink = "help_link" -) - -func GetPrivacyPolicy() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - privacyPolicyOrgIdVar: { - Type: schema.TypeString, - Required: true, - Description: "Id for the organization", - ForceNew: true, - }, - privacyPolicyTOSLink: { - Type: schema.TypeString, - Required: true, - Description: "", - }, - privacyPolicyPrivacyLink: { - Type: schema.TypeString, - Required: true, - Description: "", - }, - privacyPolicyIsDefault: { - Type: schema.TypeBool, - Computed: true, - Description: "", - }, - privacyPolicyHelpLink: { - Type: schema.TypeString, - Required: true, - Description: "", - }, - }, - CreateContext: createPrivacyPolicy, - DeleteContext: deletePrivacyPolicy, - ReadContext: readPrivacyPolicy, - UpdateContext: updatePrivacyPolicy, - } -} - -func deletePrivacyPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(privacyPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.ResetPrivacyPolicyToDefault(ctx, &management2.ResetPrivacyPolicyToDefaultRequest{}) - if err != nil { - return diag.Errorf("failed to reset privacy policy: %v", err) - } - d.SetId(org) - return nil -} - -func updatePrivacyPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(privacyPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateCustomPrivacyPolicy(ctx, &management2.UpdateCustomPrivacyPolicyRequest{ - TosLink: d.Get(privacyPolicyTOSLink).(string), - PrivacyLink: d.Get(privacyPolicyPrivacyLink).(string), - HelpLink: d.Get(privacyPolicyHelpLink).(string), - }) - if err != nil { - return diag.Errorf("failed to update privacy policy: %v", err) - } - d.SetId(org) - return nil -} - -func createPrivacyPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(privacyPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.AddCustomPrivacyPolicy(ctx, &management2.AddCustomPrivacyPolicyRequest{ - TosLink: d.Get(privacyPolicyTOSLink).(string), - PrivacyLink: d.Get(privacyPolicyPrivacyLink).(string), - HelpLink: d.Get(privacyPolicyHelpLink).(string), - }) - if err != nil { - return diag.Errorf("failed to create privacy policy: %v", err) - } - d.SetId(org) - return nil -} - -func readPrivacyPolicy(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(privacyPolicyOrgIdVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetPrivacyPolicy(ctx, &management2.GetPrivacyPolicyRequest{}) - if err != nil { - return diag.Errorf("failed to get privacy policy: %v", err) - } - - policy := resp.Policy - set := map[string]interface{}{ - privacyPolicyOrgIdVar: policy.GetDetails().GetResourceOwner(), - privacyPolicyIsDefault: policy.GetIsDefault(), - privacyPolicyTOSLink: policy.GetTosLink(), - privacyPolicyPrivacyLink: policy.GetPrivacyLink(), - privacyPolicyHelpLink: policy.GetHelpLink(), - } - - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of privacy policy: %v", k, err) - } - } - d.SetId(policy.GetDetails().GetResourceOwner()) - return nil -} diff --git a/zitadel/v2/project.go b/zitadel/v2/project.go deleted file mode 100644 index f25a0434..00000000 --- a/zitadel/v2/project.go +++ /dev/null @@ -1,193 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" - "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/project" -) - -const ( - projectIdVar = "id" - projectNameVar = "name" - projectStateVar = "state" - projectOrgIDVar = "org_id" - projectRoleAssertionVar = "project_role_assertion" - projectRoleCheckVar = "project_role_check" - projectHasProjectCheckVar = "has_project_check" - projectPrivateLabelingSettingVar = "private_labeling_setting" -) - -func GetProject() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - projectIdVar: { - Type: schema.TypeString, - Computed: true, - Description: "ID of the project", - ForceNew: true, - }, - projectNameVar: { - Type: schema.TypeString, - Required: true, - Description: "Name of the project", - }, - projectOrgIDVar: { - Type: schema.TypeString, - Required: true, - Description: "Organization in which the project is located", - }, - projectStateVar: { - Type: schema.TypeString, - Computed: true, - Description: "State of the project", - }, - projectRoleAssertionVar: { - Type: schema.TypeBool, - Optional: true, - Description: "describes if roles of user should be added in token", - }, - projectRoleCheckVar: { - Type: schema.TypeBool, - Optional: true, - Description: "ZITADEL checks if the user has at least one on this project", - }, - projectHasProjectCheckVar: { - Type: schema.TypeBool, - Optional: true, - Description: "ZITADEL checks if the org of the user has permission to this project", - }, - projectPrivateLabelingSettingVar: { - Type: schema.TypeString, - Optional: true, - Description: "Defines from where the private labeling should be triggered", - }, - }, - DeleteContext: deleteProject, - CreateContext: createProject, - UpdateContext: updateProject, - ReadContext: readProject, - } -} - -func deleteProject(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started delete") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.RemoveProject(ctx, &management2.RemoveProjectRequest{ - Id: d.Id(), - }) - if err != nil { - return diag.Errorf("failed to delete project: %v", err) - } - return nil -} - -func updateProject(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - plSetting := d.Get(projectPrivateLabelingSettingVar).(string) - _, err = client.UpdateProject(ctx, &management2.UpdateProjectRequest{ - Id: d.Id(), - Name: d.Get(projectNameVar).(string), - ProjectRoleCheck: d.Get(projectRoleCheckVar).(bool), - ProjectRoleAssertion: d.Get(projectRoleAssertionVar).(bool), - HasProjectCheck: d.Get(projectHasProjectCheckVar).(bool), - PrivateLabelingSetting: project.PrivateLabelingSetting(project.PrivateLabelingSetting_value[plSetting]), - }) - if err != nil { - return diag.Errorf("failed to update project: %v", err) - } - - return nil -} - -func createProject(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - plSetting := d.Get(projectPrivateLabelingSettingVar).(string) - resp, err := client.AddProject(ctx, &management2.AddProjectRequest{ - Name: d.Get(projectNameVar).(string), - ProjectRoleAssertion: d.Get(projectRoleAssertionVar).(bool), - ProjectRoleCheck: d.Get(projectRoleCheckVar).(bool), - HasProjectCheck: d.Get(projectHasProjectCheckVar).(bool), - PrivateLabelingSetting: project.PrivateLabelingSetting(project.PrivateLabelingSetting_value[plSetting]), - }) - if err != nil { - return diag.Errorf("failed to create project: %v", err) - } - d.SetId(resp.GetId()) - return nil -} - -func readProject(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetProjectByID(ctx, &management2.GetProjectByIDRequest{Id: d.Id()}) - if err != nil { - d.SetId("") - return nil - //return diag.Errorf("failed to read project: %v", err) - } - - project := resp.GetProject() - set := map[string]interface{}{ - projectIdVar: project.GetId(), - projectOrgIDVar: project.GetDetails().GetResourceOwner(), - projectStateVar: project.GetState().String(), - projectNameVar: project.GetName(), - projectRoleAssertionVar: project.GetProjectRoleAssertion(), - projectRoleCheckVar: project.GetProjectRoleCheck(), - projectHasProjectCheckVar: project.GetHasProjectCheck(), - projectPrivateLabelingSettingVar: project.PrivateLabelingSetting.String(), - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of project: %v", k, err) - } - } - d.SetId(project.GetId()) - - return nil -} diff --git a/zitadel/v2/project_grant.go b/zitadel/v2/project_grant.go deleted file mode 100644 index 77791796..00000000 --- a/zitadel/v2/project_grant.go +++ /dev/null @@ -1,165 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" -) - -const ( - projectGrantOrgIDVar = "org_id" - projectGrantProjectIDVar = "project_id" - projectGrantGrantedOrgIDVar = "granted_org_id" - projectGrantRoleKeysVar = "role_keys" -) - -func GetProjectGrant() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - projectGrantProjectIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the project", - ForceNew: true, - }, - projectGrantGrantedOrgIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization granted the project", - ForceNew: true, - }, - projectGrantRoleKeysVar: { - Type: schema.TypeSet, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Optional: true, - Description: "List of roles granted", - }, - projectGrantOrgIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization which owns the resource", - }, - }, - DeleteContext: deleteProjectGrant, - CreateContext: createProjectGrant, - UpdateContext: updateProjectGrant, - ReadContext: readProjectGrant, - } -} - -func deleteProjectGrant(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started delete") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectGrantOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.RemoveProjectGrant(ctx, &management2.RemoveProjectGrantRequest{ - GrantId: d.Id(), - ProjectId: d.Get(projectGrantProjectIDVar).(string), - }) - if err != nil { - return diag.Errorf("failed to delete projectgrant: %v", err) - } - return nil -} - -func updateProjectGrant(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectGrantOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateProjectGrant(ctx, &management2.UpdateProjectGrantRequest{ - GrantId: d.Id(), - ProjectId: d.Get(projectGrantProjectIDVar).(string), - RoleKeys: d.Get(projectGrantRoleKeysVar).([]string), - }) - if err != nil { - return diag.Errorf("failed to update projectgrant: %v", err) - } - return nil -} - -func createProjectGrant(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectGrantOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - roles := make([]string, 0) - for _, role := range d.Get(projectGrantRoleKeysVar).(*schema.Set).List() { - roles = append(roles, role.(string)) - } - - resp, err := client.AddProjectGrant(ctx, &management2.AddProjectGrantRequest{ - GrantedOrgId: d.Get(projectGrantGrantedOrgIDVar).(string), - ProjectId: d.Get(projectGrantProjectIDVar).(string), - RoleKeys: roles, - }) - if err != nil { - return diag.Errorf("failed to create projectgrant: %v", err) - } - d.SetId(resp.GetGrantId()) - return nil -} - -func readProjectGrant(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectGrantOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetProjectGrantByID(ctx, &management2.GetProjectGrantByIDRequest{ProjectId: d.Get(projectGrantProjectIDVar).(string), GrantId: d.Id()}) - if err != nil { - d.SetId("") - return nil - //return diag.Errorf("failed to read projectgrant: %v", err) - } - - projectGrant := resp.GetProjectGrant() - set := map[string]interface{}{ - projectGrantProjectIDVar: projectGrant.GetProjectId(), - projectGrantGrantedOrgIDVar: projectGrant.GetGrantedOrgId(), - projectGrantRoleKeysVar: projectGrant.GetGrantedRoleKeys(), - projectGrantOrgIDVar: projectGrant.GetDetails().GetResourceOwner(), - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of projectgrant: %v", k, err) - } - } - d.SetId(projectGrant.GetGrantId()) - return nil -} diff --git a/zitadel/v2/project_grant_member.go b/zitadel/v2/project_grant_member.go deleted file mode 100644 index eb77b06b..00000000 --- a/zitadel/v2/project_grant_member.go +++ /dev/null @@ -1,189 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" -) - -const ( - projectGrantMemberOrgIDVar = "org_id" - projectGrantMemberProjectIDVar = "project_id" - projectGrantMemberGrantIDVar = "grant_id" - projectGrantMemberUserIDVar = "user_id" - projectGrantMemberRolesVar = "roles" -) - -func GetProjectGrantMember() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - projectGrantMemberOrgIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization which owns the resource", - ForceNew: true, - }, - projectGrantMemberProjectIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the project", - ForceNew: true, - }, - projectGrantMemberGrantIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the grant", - ForceNew: true, - }, - projectGrantMemberUserIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the user", - ForceNew: true, - }, - projectGrantMemberRolesVar: { - Type: schema.TypeSet, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Required: true, - Description: "List of roles granted", - }, - }, - DeleteContext: deleteProjectGrantMember, - CreateContext: createProjectGrantMember, - UpdateContext: updateProjectGrantMember, - ReadContext: readProjectGrantMember, - } -} - -func deleteProjectGrantMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started delete") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectGrantMemberOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.RemoveProjectGrantMember(ctx, &management2.RemoveProjectGrantMemberRequest{ - UserId: d.Get(projectGrantMemberUserIDVar).(string), - ProjectId: d.Get(projectGrantMemberProjectIDVar).(string), - GrantId: d.Get(projectGrantMemberGrantIDVar).(string), - }) - if err != nil { - return diag.Errorf("failed to delete projectmember: %v", err) - } - return nil -} - -func updateProjectGrantMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectGrantMemberOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateProjectGrantMember(ctx, &management2.UpdateProjectGrantMemberRequest{ - UserId: d.Get(projectGrantMemberUserIDVar).(string), - Roles: d.Get(projectGrantMemberRolesVar).([]string), - ProjectId: d.Get(projectGrantMemberProjectIDVar).(string), - GrantId: d.Get(projectGrantMemberGrantIDVar).(string), - }) - if err != nil { - return diag.Errorf("failed to update projectmember: %v", err) - } - return nil -} - -func createProjectGrantMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(projectGrantMemberOrgIDVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - userID := d.Get(projectGrantMemberUserIDVar).(string) - projectID := d.Get(projectGrantMemberProjectIDVar).(string) - grantID := d.Get(projectGrantMemberGrantIDVar).(string) - roles := make([]string, 0) - for _, role := range d.Get(projectGrantMemberRolesVar).(*schema.Set).List() { - roles = append(roles, role.(string)) - } - _, err = client.AddProjectGrantMember(ctx, &management2.AddProjectGrantMemberRequest{ - UserId: userID, - ProjectId: projectID, - GrantId: grantID, - Roles: roles, - }) - if err != nil { - return diag.Errorf("failed to create projectgrantmember: %v", err) - } - d.SetId(getProjectGrantMemberID(org, projectID, grantID, userID)) - return nil -} - -func readProjectGrantMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - org := d.Get(projectGrantMemberOrgIDVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - projectID := d.Get(projectGrantMemberProjectIDVar).(string) - grantID := d.Get(projectGrantMemberGrantIDVar).(string) - resp, err := client.ListProjectGrantMembers(ctx, &management2.ListProjectGrantMembersRequest{ProjectId: projectID, GrantId: grantID}) - if err != nil { - return diag.Errorf("failed to read projectgrantmember: %v", err) - } - - userID := d.Get(projectGrantMemberUserIDVar).(string) - for _, member := range resp.Result { - if member.UserId == userID { - set := map[string]interface{}{ - projectGrantMemberUserIDVar: member.GetUserId(), - projectGrantMemberOrgIDVar: member.GetDetails().GetResourceOwner(), - projectGrantMemberProjectIDVar: projectID, - projectGrantMemberRolesVar: member.GetRoles(), - projectGrantMemberGrantIDVar: grantID, - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of projectgrantmember: %v", k, err) - } - } - d.SetId(getProjectGrantMemberID(org, projectID, grantID, userID)) - return nil - } - } - return nil -} - -func getProjectGrantMemberID(org, projectID, grantID, userID string) string { - return org + "_" + projectID + "_" + grantID + "_" + userID -} diff --git a/zitadel/v2/project_member.go b/zitadel/v2/project_member.go deleted file mode 100644 index 35af3167..00000000 --- a/zitadel/v2/project_member.go +++ /dev/null @@ -1,177 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" -) - -const ( - projectMemberOrgIDVar = "org_id" - projectMemberProjectIDVar = "project_id" - projectMemberUserIDVar = "user_id" - projectMemberRolesVar = "roles" -) - -func GetProjectMember() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - projectMemberOrgIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization which owns the resource", - ForceNew: true, - }, - projectMemberProjectIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the project", - ForceNew: true, - }, - projectMemberUserIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the user", - ForceNew: true, - }, - projectMemberRolesVar: { - Type: schema.TypeSet, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Required: true, - Description: "List of roles granted", - }, - }, - DeleteContext: deleteProjectMember, - CreateContext: createProjectMember, - UpdateContext: updateProjectMember, - ReadContext: readProjectMember, - } -} - -func deleteProjectMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started delete") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectMemberOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.RemoveProjectMember(ctx, &management2.RemoveProjectMemberRequest{ - UserId: d.Get(projectMemberUserIDVar).(string), - ProjectId: d.Get(projectMemberProjectIDVar).(string), - }) - if err != nil { - return diag.Errorf("failed to delete projectmember: %v", err) - } - return nil -} - -func updateProjectMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectMemberOrgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateProjectMember(ctx, &management2.UpdateProjectMemberRequest{ - UserId: d.Get(projectMemberUserIDVar).(string), - Roles: d.Get(projectMemberRolesVar).([]string), - ProjectId: d.Get(projectMemberProjectIDVar).(string), - }) - if err != nil { - return diag.Errorf("failed to update projectmember: %v", err) - } - return nil -} - -func createProjectMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - org := d.Get(projectMemberOrgIDVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - userID := d.Get(projectMemberUserIDVar).(string) - projectID := d.Get(projectMemberProjectIDVar).(string) - roles := make([]string, 0) - for _, role := range d.Get(projectMemberRolesVar).(*schema.Set).List() { - roles = append(roles, role.(string)) - } - - _, err = client.AddProjectMember(ctx, &management2.AddProjectMemberRequest{ - UserId: userID, - ProjectId: projectID, - Roles: roles, - }) - if err != nil { - return diag.Errorf("failed to create projectmember: %v", err) - } - d.SetId(getProjectMemberID(org, projectID, userID)) - return nil -} - -func readProjectMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - org := d.Get(projectMemberOrgIDVar).(string) - client, err := getManagementClient(clientinfo, org) - if err != nil { - return diag.FromErr(err) - } - - projectID := d.Get(projectMemberProjectIDVar).(string) - resp, err := client.ListProjectMembers(ctx, &management2.ListProjectMembersRequest{ProjectId: projectID}) - if err != nil { - return diag.Errorf("failed to read projectmember: %v", err) - } - - userID := d.Get(projectMemberUserIDVar).(string) - for _, member := range resp.Result { - if member.UserId == userID { - set := map[string]interface{}{ - projectMemberUserIDVar: member.GetUserId(), - projectMemberOrgIDVar: member.GetDetails().GetResourceOwner(), - projectMemberProjectIDVar: projectID, - projectMemberRolesVar: member.GetRoles(), - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of projectmember: %v", k, err) - } - } - d.SetId(getProjectMemberID(org, projectID, userID)) - return nil - } - } - return nil -} - -func getProjectMemberID(org string, projectID string, userID string) string { - return org + "_" + projectID + "_" + userID -} diff --git a/zitadel/v2/project_role.go b/zitadel/v2/project_role.go deleted file mode 100644 index d7b1da9b..00000000 --- a/zitadel/v2/project_role.go +++ /dev/null @@ -1,195 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" - "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/object" - project2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/project" -) - -const ( - projectRoleOrgID = "org_id" - projectRoleProjectID = "project_id" - projectRoleKey = "role_key" - projectRoleDisplayName = "display_name" - projectRoleGroup = "group" -) - -func GetProjectRole() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - projectRoleProjectID: { - Type: schema.TypeString, - Required: true, - Description: "ID of the project", - ForceNew: true, - }, - projectRoleOrgID: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization", - ForceNew: true, - }, - projectRoleKey: { - Type: schema.TypeString, - Required: true, - Description: "Key used for project role", - }, - projectRoleDisplayName: { - Type: schema.TypeString, - Required: true, - Description: "Name used for project role", - }, - projectRoleGroup: { - Type: schema.TypeString, - Optional: true, - Description: "Group used for project role", - }, - }, - DeleteContext: deleteProjectRole, - CreateContext: createProjectRole, - UpdateContext: updateProjectRole, - ReadContext: readProjectRole, - } -} - -func deleteProjectRole(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started delete") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectRoleOrgID).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.RemoveProjectRole(ctx, &management2.RemoveProjectRoleRequest{ - ProjectId: d.Get(projectRoleProjectID).(string), - RoleKey: d.Get(projectRoleKey).(string), - }) - if err != nil { - return diag.Errorf("failed to delete project role: %v", err) - } - return nil -} - -func updateProjectRole(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectRoleOrgID).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateProjectRole(ctx, &management2.UpdateProjectRoleRequest{ - ProjectId: d.Get(projectRoleProjectID).(string), - RoleKey: d.Get(projectRoleKey).(string), - DisplayName: d.Get(projectRoleDisplayName).(string), - Group: d.Get(projectRoleGroup).(string), - }) - if err != nil { - return diag.Errorf("failed to update project role: %v", err) - } - - return nil -} - -func createProjectRole(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - orgID := d.Get(projectRoleOrgID).(string) - client, err := getManagementClient(clientinfo, orgID) - if err != nil { - return diag.FromErr(err) - } - - projectID := d.Get(projectRoleProjectID).(string) - roleKey := d.Get(projectRoleKey).(string) - _, err = client.AddProjectRole(ctx, &management2.AddProjectRoleRequest{ - ProjectId: projectID, - RoleKey: roleKey, - DisplayName: d.Get(projectRoleDisplayName).(string), - Group: d.Get(projectRoleGroup).(string), - }) - if err != nil { - return diag.Errorf("failed to create project role: %v", err) - } - d.SetId(getProjectRoleID(orgID, projectID, roleKey)) - - return nil -} - -func readProjectRole(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(projectRoleOrgID).(string)) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.ListProjectRoles(ctx, &management2.ListProjectRolesRequest{ - ProjectId: d.Get(projectRoleProjectID).(string), - Queries: []*project2.RoleQuery{ - {Query: &project2.RoleQuery_KeyQuery{ - KeyQuery: &project2.RoleKeyQuery{ - Key: d.Get(projectRoleKey).(string), - Method: object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS, - }, - }}, - }, - }) - if err != nil || resp.Result == nil || len(resp.Result) == 0 { - d.SetId("") - return nil - //return diag.Errorf("failed to read project role: %v", err) - } - - if len(resp.Result) == 1 { - projectID := d.Get(projectRoleProjectID).(string) - orgID := d.Get(projectRoleOrgID).(string) - projectRole := resp.GetResult()[0] - roleKey := projectRole.GetKey() - set := map[string]interface{}{ - projectRoleProjectID: projectID, - projectRoleOrgID: orgID, - projectRoleKey: roleKey, - projectRoleDisplayName: projectRole.GetDisplayName(), - projectRoleGroup: projectRole.GetGroup(), - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of project: %v", k, err) - } - } - d.SetId(getProjectRoleID(orgID, projectID, roleKey)) - return nil - } - - d.SetId("") - return nil -} - -func getProjectRoleID(orgID string, projectID string, roleKey string) string { - return orgID + "_" + projectID + "_" + roleKey -} diff --git a/zitadel/v2/user.go b/zitadel/v2/user.go deleted file mode 100644 index 7228ad7b..00000000 --- a/zitadel/v2/user.go +++ /dev/null @@ -1,494 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" - "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/user" -) - -const ( - orgIDVar = "org_id" - userStateVar = "state" - userNameVar = "user_name" - loginNamesVar = "login_names" - preferredLoginNameVar = "preferred_login_name" - - firstNameVar = "first_name" - lastNameVar = "last_name" - nickNameVar = "nick_name" - displayNameVar = "display_name" - preferredLanguageVar = "preferred_language" - genderVar = "gender" - - isPhoneVerifiedVar = "is_phone_verified" - emailVar = "email" - - isEmailVerifiedVar = "is_email_verified" - phoneVar = "phone" - - machineNameVar = "name" - descriptionVar = "description" - - initialPasswordVar = "initial_password" - - HumanUser = "human" - MachineUser = "machine" -) - -func GetHumanUser() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - orgIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization", - ForceNew: true, - }, - userStateVar: { - Type: schema.TypeString, - Computed: true, - Description: "State of the user", - }, - userNameVar: { - Type: schema.TypeString, - Required: true, - Description: "Username", - }, - loginNamesVar: { - Type: schema.TypeList, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Computed: true, - Description: "Loginnames", - ForceNew: true, - }, - preferredLoginNameVar: { - Type: schema.TypeString, - Computed: true, - Description: "Preferred login name", - ForceNew: true, - }, - - firstNameVar: { - Type: schema.TypeString, - Optional: true, - Description: "First name of the user", - }, - lastNameVar: { - Type: schema.TypeString, - Optional: true, - Description: "Last name of the user", - }, - nickNameVar: { - Type: schema.TypeString, - Optional: true, - Description: "Nick name of the user", - }, - displayNameVar: { - Type: schema.TypeString, - Optional: true, - Description: "DIsplay name of the user", - }, - preferredLanguageVar: { - Type: schema.TypeString, - Optional: true, - Description: "Preferred language of the user", - }, - genderVar: { - Type: schema.TypeString, - Optional: true, - Description: "Gender of the user", - }, - emailVar: { - Type: schema.TypeString, - Optional: true, - Description: "Email of the user", - }, - isEmailVerifiedVar: { - Type: schema.TypeBool, - Optional: true, - Description: "Is the email verified of the user", - }, - phoneVar: { - Type: schema.TypeString, - Optional: true, - Description: "Phone of the user", - }, - isPhoneVerifiedVar: { - Type: schema.TypeBool, - Optional: true, - Description: "Is the phone verified of the user", - }, - initialPasswordVar: { - Type: schema.TypeString, - Optional: true, - Description: "Initially set password for the user", - }, - }, - ReadContext: readHumanUser, - CreateContext: createHumanUser, - DeleteContext: deleteUser, - UpdateContext: updateHumanUser, - } -} - -func GetMachineUser() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - orgIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization", - ForceNew: true, - }, - userStateVar: { - Type: schema.TypeString, - Computed: true, - Description: "State of the user", - }, - userNameVar: { - Type: schema.TypeString, - Required: true, - Description: "Username", - }, - loginNamesVar: { - Type: schema.TypeList, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Computed: true, - Description: "Loginnames", - }, - preferredLoginNameVar: { - Type: schema.TypeString, - Computed: true, - Description: "Preferred login name", - }, - - machineNameVar: { - Type: schema.TypeString, - Optional: true, - Description: "Name of the machine user", - }, - descriptionVar: { - Type: schema.TypeString, - Optional: true, - Description: "Description of the user", - }, - }, - ReadContext: readMachineUser, - CreateContext: createMachineUser, - DeleteContext: deleteUser, - UpdateContext: updateMachineUser, - } -} - -func deleteUser(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(orgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.RemoveUser(ctx, &management2.RemoveUserRequest{ - Id: d.Id(), - }) - if err != nil { - return diag.Errorf("failed to delete user: %v", err) - } - return nil -} - -func createHumanUser(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(orgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - gender := d.Get(genderVar).(string) - respUser, err := client.AddHumanUser(ctx, &management2.AddHumanUserRequest{ - UserName: d.Get(userNameVar).(string), - Profile: &management2.AddHumanUserRequest_Profile{ - FirstName: d.Get(firstNameVar).(string), - LastName: d.Get(lastNameVar).(string), - NickName: d.Get(nickNameVar).(string), - DisplayName: d.Get(displayNameVar).(string), - PreferredLanguage: d.Get(preferredLanguageVar).(string), - Gender: user.Gender(user.Gender_value[gender]), - }, - Email: &management2.AddHumanUserRequest_Email{ - Email: d.Get(emailVar).(string), - IsEmailVerified: d.Get(isEmailVerifiedVar).(bool), - }, - Phone: &management2.AddHumanUserRequest_Phone{ - Phone: d.Get(phoneVar).(string), - IsPhoneVerified: d.Get(isPhoneVerifiedVar).(bool), - }, - }) - if err != nil { - return diag.Errorf("failed to create human user: %v", err) - } - d.SetId(respUser.UserId) - - return nil -} - -func createMachineUser(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(orgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - respUser, err := client.AddMachineUser(ctx, &management2.AddMachineUserRequest{ - UserName: d.Get(userNameVar).(string), - Name: d.Get(machineNameVar).(string), - Description: d.Get(descriptionVar).(string), - }) - if err != nil { - return diag.Errorf("failed to create machine user: %v", err) - } - d.SetId(respUser.UserId) - - return nil -} - -func updateHumanUser(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(orgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - currentUser, err := client.GetUserByID(ctx, &management2.GetUserByIDRequest{Id: d.Id()}) - if err != nil { - return diag.FromErr(err) - } - - username := d.Get(userNameVar).(string) - if currentUser.GetUser().GetUserName() != username { - _, err = client.UpdateUserName(ctx, &management2.UpdateUserNameRequest{ - UserId: d.Id(), - UserName: username, - }) - if err != nil { - return diag.Errorf("failed to update username: %v", err) - } - } - - currentHuman := currentUser.GetUser().GetHuman() - if currentHuman.GetProfile().GetFirstName() != d.Get(firstNameVar).(string) || - currentHuman.GetProfile().GetLastName() != d.Get(lastNameVar).(string) || - currentHuman.GetProfile().GetNickName() != d.Get(nickNameVar).(string) || - currentHuman.GetProfile().GetDisplayName() != d.Get(displayNameVar).(string) || - currentHuman.GetProfile().GetPreferredLanguage() != d.Get(preferredLanguageVar).(string) { - gender := d.Get(genderVar).(string) - _, err := client.UpdateHumanProfile(ctx, &management2.UpdateHumanProfileRequest{ - UserId: d.Id(), - FirstName: d.Get(firstNameVar).(string), - LastName: d.Get(lastNameVar).(string), - NickName: d.Get(nickNameVar).(string), - DisplayName: d.Get(displayNameVar).(string), - PreferredLanguage: d.Get(preferredLanguageVar).(string), - Gender: user.Gender(user.Gender_value[gender]), - }) - if err != nil { - return diag.Errorf("failed to update human profile: %v", err) - } - } - if currentHuman.GetEmail().GetEmail() != d.Get(emailVar).(string) || currentHuman.GetEmail().GetIsEmailVerified() != d.Get(isEmailVerifiedVar).(bool) { - _, err = client.UpdateHumanEmail(ctx, &management2.UpdateHumanEmailRequest{ - UserId: d.Id(), - Email: d.Get(emailVar).(string), - IsEmailVerified: d.Get(isEmailVerifiedVar).(bool), - }) - if err != nil { - return diag.Errorf("failed to update human email: %v", err) - } - } - - if currentHuman.GetPhone().GetPhone() != d.Get(phoneVar).(string) || currentHuman.GetPhone().GetIsPhoneVerified() != d.Get(isPhoneVerifiedVar).(bool) { - _, err = client.UpdateHumanPhone(ctx, &management2.UpdateHumanPhoneRequest{ - UserId: d.Id(), - Phone: d.Get(phoneVar).(string), - IsPhoneVerified: d.Get(isPhoneVerifiedVar).(bool), - }) - if err != nil { - return diag.Errorf("failed to update human phone: %v", err) - } - } - - return nil -} - -func updateMachineUser(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(orgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - currentUser, err := client.GetUserByID(ctx, &management2.GetUserByIDRequest{Id: d.Id()}) - if err != nil { - return diag.FromErr(err) - } - - username := d.Get(userNameVar).(string) - if currentUser.GetUser().GetUserName() != username { - _, err = client.UpdateUserName(ctx, &management2.UpdateUserNameRequest{ - UserId: d.Id(), - UserName: username, - }) - if err != nil { - return diag.Errorf("failed to update username: %v", err) - } - } - - currentMachine := currentUser.GetUser().GetMachine() - if currentMachine.GetName() != d.Get(machineNameVar).(string) || currentMachine.GetDescription() != d.Get(descriptionVar).(string) { - _, err := client.UpdateMachine(ctx, &management2.UpdateMachineRequest{ - UserId: d.Id(), - Name: d.Get(machineNameVar).(string), - Description: d.Get(descriptionVar).(string), - }) - if err != nil { - return diag.Errorf("failed to update machine user: %v", err) - } - } - - return nil -} - -func readHumanUser(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(orgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - respUser, err := client.GetUserByID(ctx, &management2.GetUserByIDRequest{Id: d.Id()}) - if err != nil { - d.SetId("") - return nil - //return diag.Errorf("failed to get list of users: %v", err) - } - - user := respUser.GetUser() - set := map[string]interface{}{ - orgIDVar: user.GetDetails().GetResourceOwner(), - userStateVar: user.GetState().String(), - userNameVar: user.GetUserName(), - loginNamesVar: user.GetLoginNames(), - preferredLoginNameVar: user.GetPreferredLoginName(), - } - - if human := user.GetHuman(); human != nil { - if profile := human.GetProfile(); profile != nil { - set[firstNameVar] = profile.GetFirstName() - set[lastNameVar] = profile.GetLastName() - set[displayNameVar] = profile.GetDisplayName() - set[nickNameVar] = profile.GetNickName() - set[preferredLanguageVar] = profile.GetPreferredLanguage() - if gender := profile.GetGender().String(); gender != "" { - set[genderVar] = gender - } - } - if email := human.GetEmail(); email != nil { - set[emailVar] = email.GetEmail() - set[isEmailVerifiedVar] = email.GetIsEmailVerified() - } - if phone := human.GetPhone(); phone != nil { - set[phoneVar] = phone.GetPhone() - set[isPhoneVerifiedVar] = phone.GetIsPhoneVerified() - } - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of user: %v", k, err) - } - } - d.SetId(user.GetId()) - return nil -} - -func readMachineUser(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(orgIDVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - respUser, err := client.GetUserByID(ctx, &management2.GetUserByIDRequest{Id: d.Id()}) - if err != nil { - d.SetId("") - return nil - //return diag.Errorf("failed to get list of users: %v", err) - } - - user := respUser.GetUser() - set := map[string]interface{}{ - orgIDVar: user.GetDetails().GetResourceOwner(), - userStateVar: user.GetState().String(), - userNameVar: user.GetUserName(), - loginNamesVar: user.GetLoginNames(), - preferredLoginNameVar: user.GetPreferredLoginName(), - } - if machine := user.GetMachine(); machine != nil { - set[machineNameVar] = machine.GetName() - set[descriptionVar] = machine.GetDescription() - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of user: %v", k, err) - } - } - d.SetId(user.GetId()) - return nil -} diff --git a/zitadel/v2/user_grant.go b/zitadel/v2/user_grant.go deleted file mode 100644 index 8d7bc467..00000000 --- a/zitadel/v2/user_grant.go +++ /dev/null @@ -1,169 +0,0 @@ -package v2 - -import ( - "context" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - management2 "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" -) - -const ( - userGrantProjectIDVar = "project_id" - userGrantProjectGrantIDVar = "project_grant_id" - userGrantUserIDVar = "user_id" - userGrantRoleKeysVar = "role_keys" - userGrantResourceOwnerVar = "resource_owner" -) - -func GetUserGrant() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - userGrantProjectIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the project", - ForceNew: true, - }, - userGrantProjectGrantIDVar: { - Type: schema.TypeString, - Optional: true, - Description: "ID of the granted project", - ForceNew: true, - }, - userGrantUserIDVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the user", - ForceNew: true, - }, - userGrantRoleKeysVar: { - Type: schema.TypeSet, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Required: true, - Description: "List of roles granted", - }, - userGrantResourceOwnerVar: { - Type: schema.TypeString, - Required: true, - Description: "ID of the organization which owns the resource", - }, - }, - DeleteContext: deleteUserGrant, - CreateContext: createUserGrant, - UpdateContext: updateUserGrant, - ReadContext: readUserGrant, - } -} - -func deleteUserGrant(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started delete") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(userGrantResourceOwnerVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.RemoveUserGrant(ctx, &management2.RemoveUserGrantRequest{ - GrantId: d.Id(), - UserId: d.Get(userGrantUserIDVar).(string), - }) - if err != nil { - return diag.Errorf("failed to delete usergrant: %v", err) - } - return nil -} - -func updateUserGrant(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started update") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(userGrantResourceOwnerVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - _, err = client.UpdateUserGrant(ctx, &management2.UpdateUserGrantRequest{ - GrantId: d.Id(), - UserId: d.Get(userGrantUserIDVar).(string), - RoleKeys: d.Get(userGrantRoleKeysVar).([]string), - }) - if err != nil { - return diag.Errorf("failed to update usergrant: %v", err) - } - return nil -} - -func createUserGrant(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started create") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(userGrantResourceOwnerVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.AddUserGrant(ctx, &management2.AddUserGrantRequest{ - UserId: d.Get(userGrantUserIDVar).(string), - ProjectGrantId: d.Get(userGrantProjectGrantIDVar).(string), - ProjectId: d.Get(userGrantProjectIDVar).(string), - RoleKeys: d.Get(userGrantRoleKeysVar).([]string), - }) - if err != nil { - return diag.Errorf("failed to create usergrant: %v", err) - } - d.SetId(resp.GetUserGrantId()) - return nil -} - -func readUserGrant(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, "started read") - - clientinfo, ok := m.(*ClientInfo) - if !ok { - return diag.Errorf("failed to get client") - } - - client, err := getManagementClient(clientinfo, d.Get(userGrantResourceOwnerVar).(string)) - if err != nil { - return diag.FromErr(err) - } - - resp, err := client.GetUserGrantByID(ctx, &management2.GetUserGrantByIDRequest{UserId: d.Get(userGrantUserIDVar).(string), GrantId: d.Id()}) - if err != nil { - d.SetId("") - return nil - //return diag.Errorf("failed to read usergrant: %v", err) - } - - grant := resp.GetUserGrant() - set := map[string]interface{}{ - userGrantUserIDVar: grant.GetProjectId(), - userGrantProjectIDVar: grant.GetProjectId(), - userGrantProjectGrantIDVar: grant.GetProjectGrantId(), - userGrantRoleKeysVar: grant.GetRoleKeys(), - userGrantResourceOwnerVar: grant.GetDetails().GetResourceOwner(), - } - for k, v := range set { - if err := d.Set(k, v); err != nil { - return diag.Errorf("failed to set %s of projectgrant: %v", k, err) - } - } - d.SetId(grant.GetId()) - return nil -} diff --git a/zitadel/verify_email_message_text/resource.go b/zitadel/verify_email_message_text/resource.go new file mode 100644 index 00000000..fdebb55d --- /dev/null +++ b/zitadel/verify_email_message_text/resource.go @@ -0,0 +1,251 @@ +package verify_email_message_text + +import ( + "context" + "strings" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &verifyEmailMessageTextResource{} +) + +func New() resource.Resource { + return &verifyEmailMessageTextResource{} +} + +type verifyEmailMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *verifyEmailMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_verify_email_message_text" +} + +func (r *verifyEmailMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return text.GenSchemaMessageCustomText(ctx) +} + +func (r *verifyEmailMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *verifyEmailMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomVerifyEmailMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomVerifyEmailMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *verifyEmailMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + orgID, language := getID(ctx, state) + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomVerifyEmailMessageText(ctx, &management.GetCustomVerifyEmailMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *verifyEmailMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomVerifyEmailMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomVerifyEmailMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *verifyEmailMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + orgID, language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomVerifyEmailMessageTextToDefault(ctx, &management.ResetCustomVerifyEmailMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, orgID string, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(orgID + "_" + language) + attrs[helper.OrgIDVar] = types.StringValue(orgID) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) (string, string) { + id := helper.GetStringFromAttr(ctx, obj.Attributes(), "id") + parts := strings.Split(id, "_") + if len(parts) == 2 { + return parts[0], parts[1] + } + return helper.GetStringFromAttr(ctx, obj.Attributes(), helper.OrgIDVar), helper.GetStringFromAttr(ctx, obj.Attributes(), LanguageVar) +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(plan.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(state.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} diff --git a/zitadel/verify_email_message_text/resource_test.go b/zitadel/verify_email_message_text/resource_test.go new file mode 100644 index 00000000..df61c6be --- /dev/null +++ b/zitadel/verify_email_message_text/resource_test.go @@ -0,0 +1,52 @@ +package verify_email_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/verify_email_message_text" +) + +func TestAccVerifyEmailMessageText(t *testing.T) { + resourceName := "zitadel_verify_email_message_text" + frame := test_utils.NewOrgTestFrame(t, resourceName) + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + exampleLanguage := test_utils.AttributeValue(t, verify_email_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtitle", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^\d{18}_%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("Zitadel - Verify email"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomVerifyEmailMessageText(frame, &management.GetCustomVerifyEmailMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +} diff --git a/zitadel/verify_phone_message_text/resource.go b/zitadel/verify_phone_message_text/resource.go new file mode 100644 index 00000000..3e005e9b --- /dev/null +++ b/zitadel/verify_phone_message_text/resource.go @@ -0,0 +1,251 @@ +package verify_phone_message_text + +import ( + "context" + "strings" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "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/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + textpb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/text" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zitadel/terraform-provider-zitadel/gen/github.com/zitadel/zitadel/pkg/grpc/text" + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper" +) + +const ( + LanguageVar = "language" +) + +var ( + _ resource.Resource = &verifyPhoneMessageTextResource{} +) + +func New() resource.Resource { + return &verifyPhoneMessageTextResource{} +} + +type verifyPhoneMessageTextResource struct { + clientInfo *helper.ClientInfo +} + +func (r *verifyPhoneMessageTextResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_verify_phone_message_text" +} + +func (r *verifyPhoneMessageTextResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return text.GenSchemaMessageCustomText(ctx) +} + +func (r *verifyPhoneMessageTextResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.clientInfo = req.ProviderData.(*helper.ClientInfo) +} + +func (r *verifyPhoneMessageTextResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomVerifyPhoneMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomVerifyPhoneMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to create", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *verifyPhoneMessageTextResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + orgID, language := getID(ctx, state) + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + zResp, err := client.GetCustomVerifyPhoneMessageText(ctx, &management.GetCustomVerifyPhoneMessageTextRequest{Language: language}) + if err != nil { + return + } + if zResp.CustomText.IsDefault { + return + } + + resp.Diagnostics.Append(text.CopyMessageCustomTextToTerraform(ctx, *zResp.CustomText, &state)...) + if resp.Diagnostics.HasError() { + return + } + + setID(state, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *verifyPhoneMessageTextResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + orgID, language := getPlanAttrs(ctx, req.Plan, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + var plan types.Object + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + obj := textpb.MessageCustomText{} + resp.Diagnostics.Append(text.CopyMessageCustomTextFromTerraform(ctx, plan, &obj)...) + if resp.Diagnostics.HasError() { + return + } + + jsonpb := &runtime.JSONPb{ + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + } + data, err := jsonpb.Marshal(obj) + if err != nil { + resp.Diagnostics.AddError("failed to marshal", err.Error()) + return + } + zReq := &management.SetCustomVerifyPhoneMessageTextRequest{} + if err := jsonpb.Unmarshal(data, zReq); err != nil { + resp.Diagnostics.AddError("failed to unmarshal", err.Error()) + return + } + zReq.Language = language + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.SetCustomVerifyPhoneMessageText(ctx, zReq) + if err != nil { + resp.Diagnostics.AddError("failed to update", err.Error()) + return + } + + setID(plan, orgID, language) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *verifyPhoneMessageTextResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + orgID, language := getStateAttrs(ctx, req.State, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + client, err := helper.GetManagementClient(r.clientInfo, orgID) + if err != nil { + resp.Diagnostics.AddError("failed to get client", err.Error()) + return + } + + _, err = client.ResetCustomVerifyPhoneMessageTextToDefault(ctx, &management.ResetCustomVerifyPhoneMessageTextToDefaultRequest{Language: language}) + if err != nil { + resp.Diagnostics.AddError("failed to delete", err.Error()) + return + } +} + +func setID(obj types.Object, orgID string, language string) { + attrs := obj.Attributes() + attrs["id"] = types.StringValue(orgID + "_" + language) + attrs[helper.OrgIDVar] = types.StringValue(orgID) + attrs[LanguageVar] = types.StringValue(language) +} + +func getID(ctx context.Context, obj types.Object) (string, string) { + id := helper.GetStringFromAttr(ctx, obj.Attributes(), "id") + parts := strings.Split(id, "_") + if len(parts) == 2 { + return parts[0], parts[1] + } + return helper.GetStringFromAttr(ctx, obj.Attributes(), helper.OrgIDVar), helper.GetStringFromAttr(ctx, obj.Attributes(), LanguageVar) +} + +func getPlanAttrs(ctx context.Context, plan tfsdk.Plan, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(plan.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(plan.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} + +func getStateAttrs(ctx context.Context, state tfsdk.State, diag diag.Diagnostics) (string, string) { + var orgID string + diag.Append(state.GetAttribute(ctx, path.Root(helper.OrgIDVar), &orgID)...) + if diag.HasError() { + return "", "" + } + var language string + diag.Append(state.GetAttribute(ctx, path.Root(LanguageVar), &language)...) + if diag.HasError() { + return "", "" + } + + return orgID, language +} diff --git a/zitadel/verify_phone_message_text/resource_test.go b/zitadel/verify_phone_message_text/resource_test.go new file mode 100644 index 00000000..a4615529 --- /dev/null +++ b/zitadel/verify_phone_message_text/resource_test.go @@ -0,0 +1,51 @@ +package verify_phone_message_text_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" + + "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils" + "github.com/zitadel/terraform-provider-zitadel/zitadel/verify_phone_message_text" +) + +func TestAccVerifyPhoneMessageText(t *testing.T) { + frame := test_utils.NewOrgTestFrame(t, "zitadel_verify_phone_message_text") + resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType) + exampleProperty := test_utils.AttributeValue(t, "title", exampleAttributes).AsString() + exampleLanguage := test_utils.AttributeValue(t, verify_phone_message_text.LanguageVar, exampleAttributes).AsString() + test_utils.RunLifecyleTest( + t, + frame.BaseTestFrame, + []string{frame.AsOrgDefaultDependency}, + test_utils.ReplaceAll(resourceExample, exampleProperty, ""), + exampleProperty, "updatedtitle", + "", "", "", + true, + checkRemoteProperty(frame, exampleLanguage), + regexp.MustCompile(fmt.Sprintf(`^\d{18}_%s$`, exampleLanguage)), + // When deleted, the default should be returned + checkRemoteProperty(frame, exampleLanguage)("Zitadel - Verify phone"), + nil, + ) +} + +func checkRemoteProperty(frame *test_utils.OrgTestFrame, lang string) func(string) resource.TestCheckFunc { + return func(expect string) resource.TestCheckFunc { + return func(state *terraform.State) error { + remoteResource, err := frame.GetCustomVerifyPhoneMessageText(frame, &management.GetCustomVerifyPhoneMessageTextRequest{Language: lang}) + if err != nil { + return err + } + actual := remoteResource.GetCustomText().GetTitle() + if actual != expect { + return fmt.Errorf("expected %s, but got %s", expect, actual) + } + return nil + } + } +}