7 Commits

Author SHA1 Message Date
0208cbd960 #5 fixed state handling for public keys
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2022-07-08 21:50:09 +02:00
08dffb3e3a Merge pull request '#3 added team resource and fixed crash in public key state persisting' (#4) from feature/tt/#3-team-management into main
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #4
2022-06-25 22:38:05 +00:00
680e2dcba2 #3 teams can now have members
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-06-26 00:35:28 +02:00
6e63797167 #3 added team resource and fixed crash in public key state persisting
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-06-25 23:36:24 +02:00
787a2b9636 added MIT license
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-25 22:00:22 +02:00
39198a40d6 hopefully fixed drone signing
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2022-06-21 23:12:49 +02:00
26eb2c104a added ssh key management resource
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-21 23:10:25 +02:00
15 changed files with 650 additions and 4 deletions

View File

@ -45,6 +45,8 @@ steps:
from_secret: GPG_PRIVATE_KEY from_secret: GPG_PRIVATE_KEY
GPG_FINGERPRINT: GPG_FINGERPRINT:
from_secret: GPG_FINGERPRINT from_secret: GPG_FINGERPRINT
GPG_PRIVATE_KEY_BASE64:
from_secret: GPG_PRIVATE_KEY_BASE64
commands: commands:
- apk add gpg-agent - apk add gpg-agent
- gpg-agent --daemon --default-cache-ttl 7200 - gpg-agent --daemon --default-cache-ttl 7200

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2022 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -3,7 +3,7 @@ GOFMT_FILES?=$$(find . -name '*.go' |grep -v vendor)
GOFMT ?= gofmt -s GOFMT ?= gofmt -s
VERSION = 0.4.0 VERSION = 0.6.1
test: fmt-check test: fmt-check
go test -i $(TEST) || exit 1 go test -i $(TEST) || exit 1
@ -37,3 +37,5 @@ install: build
@echo ~/.terraform.d/plugins/terraform.local/lerentis/gitea/${VERSION}/linux_amd64/terraform-provider-gitea_${VERSION} @echo ~/.terraform.d/plugins/terraform.local/lerentis/gitea/${VERSION}/linux_amd64/terraform-provider-gitea_${VERSION}
@mkdir -p ~/.terraform.d/plugins/terraform.local/lerentis/gitea/${VERSION}/linux_amd64 @mkdir -p ~/.terraform.d/plugins/terraform.local/lerentis/gitea/${VERSION}/linux_amd64
@mv terraform-provider-gitea_${VERSION} ~/.terraform.d/plugins/terraform.local/lerentis/gitea/${VERSION}/linux_amd64/terraform-provider-gitea_${VERSION} @mv terraform-provider-gitea_${VERSION} ~/.terraform.d/plugins/terraform.local/lerentis/gitea/${VERSION}/linux_amd64/terraform-provider-gitea_${VERSION}
doc:
tfplugindocs

View File

@ -0,0 +1,52 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "gitea_public_key Resource - terraform-provider-gitea"
subcategory: ""
description: |-
gitea_public_key manages ssh key that are associated with users.
---
# gitea_public_key (Resource)
`gitea_public_key` manages ssh key that are associated with users.
## Example Usage
```terraform
resource "gitea_user" "test" {
username = "test"
login_name = "test"
password = "Geheim1!"
email = "test@user.dev"
must_change_password = false
}
resource "gitea_public_key" "test_user_key" {
title = "test"
key = file("${path.module}/id_ed25519.pub")
username = gitea_user.test.username
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `key` (String, Sensitive) An armored SSH key to add
- `title` (String) Title of the key to add
- `username` (String) User to associate with the added key
### Optional
- `read_only` (Boolean) Describe if the key has only read access or read/write
### Read-Only
- `created` (String)
- `fingerprint` (String)
- `id` (String) The ID of this resource.
- `type` (String)

62
docs/resources/team.md Normal file
View File

@ -0,0 +1,62 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "gitea_team Resource - terraform-provider-gitea"
subcategory: ""
description: |-
gitea_team manages Team that are part of an organisation.
---
# gitea_team (Resource)
`gitea_team` manages Team that are part of an organisation.
## Example Usage
```terraform
resource "gitea_org" "test_org" {
name = "test-org"
}
resource "gitea_user" "test" {
username = "test"
login_name = "test"
password = "Geheim1!"
email = "test@user.dev"
must_change_password = false
admin = true
}
resource "gitea_team" "test_team" {
name = "Devs"
organisation = gitea_org.test_org.name
description = "Devs of Test Org"
permission = "write"
members = [gitea_user.test.username]
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `name` (String) Name of the Team
- `organisation` (String) The organisation which this Team is part of.
### Optional
- `can_create_repos` (Boolean) Flag if the Teams members should be able to create Rpositories in the Organisation
- `description` (String) Description of the Team
- `include_all_repositories` (Boolean) Flag if the Teams members should have access to all Repositories in the Organisation
- `members` (List of String) List of Users that should be part of this team
- `permission` (String) Permissions associated with this Team
Can be `none`, `read`, `write`, `admin` or `owner`
- `units` (String) List of types of Repositories that should be allowed to be created from Team members.
Can be `repo.code`, `repo.issues`, `repo.ext_issues`, `repo.wiki`, `repo.pulls`, `repo.releases`, `repo.projects` and/or `repo.ext_wiki`
### Read-Only
- `id` (String) The ID of this resource.

1
examples/.gitignore vendored
View File

@ -3,3 +3,4 @@
terraform.tfstate terraform.tfstate
terraform.tfstate.backup terraform.tfstate.backup
*.tfvars *.tfvars
id_ed25519

View File

@ -38,3 +38,20 @@ resource "gitea_user" "test" {
must_change_password = false must_change_password = false
admin = true admin = true
} }
resource "gitea_public_key" "test_user_key" {
title = "test"
key = file("${path.module}/resources/gitea_public_key/id_ed25519.pub")
read_only = true
username = gitea_user.test.username
}
resource "gitea_team" "test_team" {
name = "Devs"
organisation = gitea_org.test_org.name
description = "Devs of Test Org"
permission = "write"
members = [gitea_user.test.username]
}

View File

@ -2,7 +2,7 @@ terraform {
required_providers { required_providers {
gitea = { gitea = {
source = "terraform.local/lerentis/gitea" source = "terraform.local/lerentis/gitea"
version = "0.4.0" version = "0.6.1"
} }
} }
} }

View File

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINn6hAP48oKz6MVWjYvn0fne2YeaOv/zC6zuvFXlJKf2 test@dev.local

View File

@ -0,0 +1,14 @@
resource "gitea_user" "test" {
username = "test"
login_name = "test"
password = "Geheim1!"
email = "test@user.dev"
must_change_password = false
}
resource "gitea_public_key" "test_user_key" {
title = "test"
key = file("${path.module}/id_ed25519.pub")
username = gitea_user.test.username
}

View File

@ -0,0 +1,21 @@
resource "gitea_org" "test_org" {
name = "test-org"
}
resource "gitea_user" "test" {
username = "test"
login_name = "test"
password = "Geheim1!"
email = "test@user.dev"
must_change_password = false
admin = true
}
resource "gitea_team" "test_team" {
name = "Devs"
organisation = gitea_org.test_org.name
description = "Devs of Test Org"
permission = "write"
members = [gitea_user.test.username]
}

View File

@ -79,6 +79,8 @@ func Provider() terraform.ResourceProvider {
"gitea_user": resourceGiteaUser(), "gitea_user": resourceGiteaUser(),
"gitea_oauth2_app": resourceGiteaOauthApp(), "gitea_oauth2_app": resourceGiteaOauthApp(),
"gitea_repository": resourceGiteaRepository(), "gitea_repository": resourceGiteaRepository(),
"gitea_public_key": resourceGiteaPublicKey(),
"gitea_team": resourceGiteaTeam(),
}, },
ConfigureFunc: providerConfigure, ConfigureFunc: providerConfigure,

View File

@ -0,0 +1,155 @@
package gitea
import (
"fmt"
"strconv"
"code.gitea.io/sdk/gitea"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
const (
PublicKeyUser string = "username"
PublicKey string = "key"
PublicKeyReadOnlyFlag string = "read_only"
PublicKeyTitle string = "title"
PublicKeyId string = "id"
PublicKeyFingerprint string = "fingerprint"
PublicKeyCreated string = "created"
PublicKeyType string = "type"
)
func resourcePublicKeyRead(d *schema.ResourceData, meta interface{}) (err error) {
client := meta.(*gitea.Client)
id, err := strconv.ParseInt(d.Id(), 10, 64)
var resp *gitea.Response
var pubKey *gitea.PublicKey
pubKey, resp, err = client.GetPublicKey(id)
if err != nil {
if resp.StatusCode == 404 {
d.SetId("")
return nil
} else {
return err
}
}
err = setPublicKeyResourceData(pubKey, d)
return
}
func resourcePublicKeyCreate(d *schema.ResourceData, meta interface{}) (err error) {
client := meta.(*gitea.Client)
var pubKey *gitea.PublicKey
opts := gitea.CreateKeyOption{
Title: d.Get(PublicKeyTitle).(string),
Key: d.Get(PublicKey).(string),
ReadOnly: d.Get(PublicKeyReadOnlyFlag).(bool),
}
pubKey, _, err = client.AdminCreateUserPublicKey(d.Get(PublicKeyUser).(string), opts)
err = setPublicKeyResourceData(pubKey, d)
return
}
func resourcePublicKeyUpdate(d *schema.ResourceData, meta interface{}) (err error) {
// update = recreate
resourcePublicKeyDelete(d, meta)
resourcePublicKeyCreate(d, meta)
return
}
func resourcePublicKeyDelete(d *schema.ResourceData, meta interface{}) (err error) {
client := meta.(*gitea.Client)
id, err := strconv.ParseInt(d.Id(), 10, 64)
var resp *gitea.Response
resp, err = client.AdminDeleteUserPublicKey(d.Get(PublicKeyUser).(string), int(id))
if err != nil {
if resp.StatusCode == 404 {
return
} else {
return err
}
}
return
}
func setPublicKeyResourceData(pubKey *gitea.PublicKey, d *schema.ResourceData) (err error) {
d.SetId(fmt.Sprintf("%d", pubKey.ID))
d.Set(PublicKeyUser, d.Get(PublicKeyUser).(string))
d.Set(PublicKey, d.Get(PublicKey).(string))
d.Set(PublicKeyTitle, pubKey.Title)
d.Set(PublicKeyReadOnlyFlag, d.Get(PublicKeyReadOnlyFlag).(bool))
d.Set(PublicKeyCreated, pubKey.Created)
d.Set(PublicKeyFingerprint, pubKey.Fingerprint)
d.Set(PublicKeyType, pubKey.KeyType)
return
}
func resourceGiteaPublicKey() *schema.Resource {
return &schema.Resource{
Read: resourcePublicKeyRead,
Create: resourcePublicKeyCreate,
Update: resourcePublicKeyUpdate,
Delete: resourcePublicKeyDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"title": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Title of the key to add",
},
"key": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Sensitive: true,
Description: "An armored SSH key to add",
},
"read_only": {
Type: schema.TypeBool,
Required: false,
Optional: true,
Default: false,
Description: "Describe if the key has only read access or read/write",
},
"username": {
Type: schema.TypeString,
Required: true,
Optional: false,
ForceNew: true,
Description: "User to associate with the added key",
},
"fingerprint": {
Type: schema.TypeString,
Computed: true,
},
"created": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
},
Description: "`gitea_public_key` manages ssh key that are associated with users.",
}
}

View File

@ -0,0 +1,296 @@
package gitea
import (
"fmt"
"strconv"
"strings"
"code.gitea.io/sdk/gitea"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
const (
TeamName string = "name"
TeamOrg string = "organisation"
TeamDescription string = "description"
TeamPermissions string = "permission"
TeamCreateRepoFlag string = "can_create_repos"
TeamIncludeAllReposFlag string = "include_all_repositories"
TeamUnits string = "units"
TeamMembers string = "members"
)
func resourceTeamRead(d *schema.ResourceData, meta interface{}) (err error) {
client := meta.(*gitea.Client)
id, err := strconv.ParseInt(d.Id(), 10, 64)
var resp *gitea.Response
var team *gitea.Team
team, resp, err = client.GetTeam(id)
if err != nil {
if resp.StatusCode == 404 {
d.SetId("")
return nil
} else {
return err
}
}
err = setTeamResourceData(team, d)
return
}
func resourceTeamCreate(d *schema.ResourceData, meta interface{}) (err error) {
client := meta.(*gitea.Client)
var team *gitea.Team
var units []gitea.RepoUnitType
if strings.Contains(d.Get(TeamUnits).(string), "repo.code") {
units = append(units, gitea.RepoUnitCode)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.issues") {
units = append(units, gitea.RepoUnitIssues)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.ext_issues") {
units = append(units, gitea.RepoUnitExtIssues)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.wiki") {
units = append(units, gitea.RepoUnitWiki)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.pulls") {
units = append(units, gitea.RepoUnitPulls)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.releases") {
units = append(units, gitea.RepoUnitReleases)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.ext_wiki") {
units = append(units, gitea.RepoUnitExtWiki)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.projects") {
units = append(units, gitea.RepoUnitProjects)
}
opts := gitea.CreateTeamOption{
Name: d.Get(TeamName).(string),
Description: d.Get(TeamDescription).(string),
Permission: gitea.AccessMode(d.Get(TeamPermissions).(string)),
CanCreateOrgRepo: d.Get(TeamCreateRepoFlag).(bool),
IncludesAllRepositories: d.Get(TeamIncludeAllReposFlag).(bool),
Units: units,
}
team, _, err = client.CreateTeam(d.Get(TeamOrg).(string), opts)
if err != nil {
return
}
users := d.Get(TeamMembers).([]interface{})
for _, user := range users {
if user != "" {
_, err = client.AddTeamMember(team.ID, user.(string))
if err != nil {
return err
}
}
}
err = setTeamResourceData(team, d)
return
}
func resourceTeamUpdate(d *schema.ResourceData, meta interface{}) (err error) {
client := meta.(*gitea.Client)
id, err := strconv.ParseInt(d.Id(), 10, 64)
var resp *gitea.Response
var team *gitea.Team
team, resp, err = client.GetTeam(id)
if err != nil {
if resp.StatusCode == 404 {
resourceTeamCreate(d, meta)
} else {
return err
}
}
description := d.Get(TeamDescription).(string)
canCreateRepo := d.Get(TeamCreateRepoFlag).(bool)
includeAllRepos := d.Get(TeamIncludeAllReposFlag).(bool)
var units []gitea.RepoUnitType
if strings.Contains(d.Get(TeamUnits).(string), "repo.code") {
units = append(units, gitea.RepoUnitCode)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.issues") {
units = append(units, gitea.RepoUnitIssues)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.ext_issues") {
units = append(units, gitea.RepoUnitExtIssues)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.wiki") {
units = append(units, gitea.RepoUnitWiki)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.pulls") {
units = append(units, gitea.RepoUnitPulls)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.releases") {
units = append(units, gitea.RepoUnitReleases)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.ext_wiki") {
units = append(units, gitea.RepoUnitExtWiki)
}
if strings.Contains(d.Get(TeamUnits).(string), "repo.projects") {
units = append(units, gitea.RepoUnitProjects)
}
opts := gitea.EditTeamOption{
Name: d.Get(TeamName).(string),
Description: &description,
Permission: gitea.AccessMode(d.Get(TeamPermissions).(string)),
CanCreateOrgRepo: &canCreateRepo,
IncludesAllRepositories: &includeAllRepos,
Units: units,
}
resp, err = client.EditTeam(id, opts)
if err != nil {
return err
}
users := d.Get(TeamMembers).([]interface{})
for _, user := range users {
if user != "" {
_, err = client.AddTeamMember(team.ID, user.(string))
if err != nil {
return err
}
}
}
team, _, _ = client.GetTeam(id)
err = setTeamResourceData(team, d)
return
}
func resourceTeamDelete(d *schema.ResourceData, meta interface{}) (err error) {
client := meta.(*gitea.Client)
id, err := strconv.ParseInt(d.Id(), 10, 64)
var resp *gitea.Response
resp, err = client.DeleteTeam(id)
if err != nil {
if resp.StatusCode == 404 {
return
} else {
return err
}
}
return
}
func setTeamResourceData(team *gitea.Team, d *schema.ResourceData) (err error) {
d.SetId(fmt.Sprintf("%d", team.ID))
d.Set(TeamCreateRepoFlag, team.CanCreateOrgRepo)
d.Set(TeamDescription, team.Description)
d.Set(TeamName, team.Name)
d.Set(TeamPermissions, string(team.Permission))
d.Set(TeamIncludeAllReposFlag, team.IncludesAllRepositories)
d.Set(TeamUnits, d.Get(TeamUnits).(string))
d.Set(TeamOrg, d.Get(TeamOrg).(string))
d.Set(TeamMembers, d.Get(TeamMembers))
return
}
func resourceGiteaTeam() *schema.Resource {
return &schema.Resource{
Read: resourceTeamRead,
Create: resourceTeamCreate,
Update: resourceTeamUpdate,
Delete: resourceTeamDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Name of the Team",
},
"organisation": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The organisation which this Team is part of.",
},
"description": {
Type: schema.TypeString,
Required: false,
Optional: true,
Default: "",
Description: "Description of the Team",
},
"permission": {
Type: schema.TypeString,
Required: false,
Optional: true,
Default: "",
Description: "Permissions associated with this Team\n" +
"Can be `none`, `read`, `write`, `admin` or `owner`",
},
"can_create_repos": {
Type: schema.TypeBool,
Required: false,
Optional: true,
Default: true,
Description: "Flag if the Teams members should be able to create Rpositories in the Organisation",
},
"include_all_repositories": {
Type: schema.TypeBool,
Required: false,
Optional: true,
Default: true,
Description: "Flag if the Teams members should have access to all Repositories in the Organisation",
},
"units": {
Type: schema.TypeString,
Required: false,
Optional: true,
Default: "[repo.code, repo.issues, repo.ext_issues, repo.wiki, repo.pulls, repo.releases, repo.projects, repo.ext_wiki]",
Description: "List of types of Repositories that should be allowed to be created from Team members.\n" +
"Can be `repo.code`, `repo.issues`, `repo.ext_issues`, `repo.wiki`, `repo.pulls`, `repo.releases`, `repo.projects` and/or `repo.ext_wiki`",
},
"members": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
Required: false,
Computed: true,
Description: "List of Users that should be part of this team",
},
},
Description: "`gitea_team` manages Team that are part of an organisation.",
}
}

View File

@ -196,7 +196,6 @@ func resourceUserDelete(d *schema.ResourceData, meta interface{}) (err error) {
func setUserResourceData(user *gitea.User, d *schema.ResourceData) (err error) { func setUserResourceData(user *gitea.User, d *schema.ResourceData) (err error) {
d.SetId(fmt.Sprintf("%d", user.ID)) d.SetId(fmt.Sprintf("%d", user.ID))
d.Set("id", user.ID)
d.Set(userName, user.UserName) d.Set(userName, user.UserName)
d.Set(userEmail, user.Email) d.Set(userEmail, user.Email)
d.Set(userFullName, user.FullName) d.Set(userFullName, user.FullName)