Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jaas-role): add jaas' role support #648

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test_integration_jaas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
uses: canonical/jimm/.github/actions/test-server@v3
id: jaas
with:
jimm-version: v3.1.10
jimm-version: v3.1.13
juju-channel: 3/stable
ghcr-pat: ${{ secrets.GITHUB_TOKEN }}
- name: Setup microk8s for juju_kubernetes_cloud test
Expand Down
34 changes: 34 additions & 0 deletions docs/data-sources/jaas_role.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "juju_jaas_role Data Source - terraform-provider-juju"
subcategory: ""
description: |-
A data source representing a Juju JAAS Role.
---

# juju_jaas_role (Data Source)

A data source representing a Juju JAAS Role.

## Example Usage

```terraform
data "juju_jaas_role" "test" {
name = "role-0"
}
output "role_uuid" {
value = data.juju_jaas_role.test.uuid
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) The name of the role.

### Read-Only

- `uuid` (String) The UUID of the role.
1 change: 1 addition & 0 deletions docs/resources/jaas_access_cloud.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ resource "juju_jaas_access_cloud" "development" {
### Optional

- `groups` (Set of String) List of groups to grant access.
- `roles` (Set of String) List of roles to grant access.
- `service_accounts` (Set of String) List of service accounts to grant access.
- `users` (Set of String) List of users to grant access.

Expand Down
1 change: 1 addition & 0 deletions docs/resources/jaas_access_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ resource "juju_jaas_access_controller" "development" {
### Optional

- `groups` (Set of String) List of groups to grant access.
- `roles` (Set of String) List of roles to grant access.
- `service_accounts` (Set of String) List of service accounts to grant access.
- `users` (Set of String) List of users to grant access.

Expand Down
1 change: 1 addition & 0 deletions docs/resources/jaas_access_model.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ resource "juju_jaas_access_model" "development" {
### Optional

- `groups` (Set of String) List of groups to grant access.
- `roles` (Set of String) List of roles to grant access.
- `service_accounts` (Set of String) List of service accounts to grant access.
- `users` (Set of String) List of users to grant access.

Expand Down
1 change: 1 addition & 0 deletions docs/resources/jaas_access_offer.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ resource "juju_jaas_access_offer" "development" {
### Optional

- `groups` (Set of String) List of groups to grant access.
- `roles` (Set of String) List of roles to grant access.
- `service_accounts` (Set of String) List of service accounts to grant access.
- `users` (Set of String) List of users to grant access.

Expand Down
50 changes: 50 additions & 0 deletions docs/resources/jaas_access_role.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "juju_jaas_access_role Resource - terraform-provider-juju"
subcategory: ""
description: |-
A resource that represents access to a role when using JAAS.
---

# juju_jaas_access_role (Resource)

A resource that represents access to a role when using JAAS.

## Example Usage

```terraform
resource "juju_jaas_access_role" "development" {
role_id = juju_jaas_role.target-role.uuid
access = "assignee"
users = ["[email protected]"]
roles = [juju_jaas_role.development.uuid]
service_accounts = ["Client-ID-1", "Client-ID-2"]
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `access` (String) Level of access to grant. Changing this value will replace the Terraform resource. Valid access levels are described at https://canonical-jaas-documentation.readthedocs-hosted.com/en/latest/reference/authorisation_model/#valid-relations
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the linked page needs to be updated with valid access levels for roles

Copy link
Contributor Author

@SimoneDutto SimoneDutto Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch, i'll propose a PR to change that page soon

- `role_id` (String) The ID of the role for access management. If this is changed the resource will be deleted and a new resource will be created.

### Optional

- `groups` (Set of String) List of groups to grant access.
- `service_accounts` (Set of String) List of service accounts to grant access.
- `users` (Set of String) List of users to grant access.

### Read-Only

- `id` (String) The ID of this resource.

## Import

Import is supported using the following syntax:

```shell
# JAAS role access can be imported using the role UUID and access level
$ terraform import juju_jaas_access_role.development UUID:assignee
```
1 change: 1 addition & 0 deletions docs/resources/jaas_access_service_account.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ resource "juju_jaas_access_service_account" "development" {
### Optional

- `groups` (Set of String) List of groups to grant access.
- `roles` (Set of String) List of roles to grant access.
- `service_accounts` (Set of String) List of service accounts to grant access.
- `users` (Set of String) List of users to grant access.

Expand Down
30 changes: 30 additions & 0 deletions docs/resources/jaas_role.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "juju_jaas_role Resource - terraform-provider-juju"
subcategory: ""
description: |-
A resource that represents a role in JAAS
---

# juju_jaas_role (Resource)

A resource that represents a role in JAAS

## Example Usage

```terraform
resource "juju_jaas_role" "development" {
name = "devops-team"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) Name of the role

### Read-Only

- `uuid` (String) UUID of the role
7 changes: 7 additions & 0 deletions examples/data-sources/juju_jaas_role/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
data "juju_jaas_role" "test" {
name = "role-0"
}

output "role_uuid" {
value = data.juju_jaas_role.test.uuid
}
2 changes: 2 additions & 0 deletions examples/resources/juju_jaas_access_role/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# JAAS role access can be imported using the role UUID and access level
$ terraform import juju_jaas_access_role.development UUID:assignee
7 changes: 7 additions & 0 deletions examples/resources/juju_jaas_access_role/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resource "juju_jaas_access_role" "development" {
role_id = juju_jaas_role.target-role.uuid
access = "assignee"
users = ["[email protected]"]
roles = [juju_jaas_role.development.uuid]
service_accounts = ["Client-ID-1", "Client-ID-2"]
}
3 changes: 3 additions & 0 deletions examples/resources/juju_jaas_role/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resource "juju_jaas_role" "development" {
name = "devops-team"
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
)

require (
github.com/canonical/jimm-go-sdk/v3 v3.0.5
github.com/canonical/jimm-go-sdk/v3 v3.0.6
github.com/dustin/go-humanize v1.0.1
github.com/hashicorp/terraform-json v0.22.1
github.com/hashicorp/terraform-plugin-framework v1.11.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZ
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/canonical/go-dqlite v1.21.0 h1:4gLDdV2GF+vg0yv9Ff+mfZZNQ1JGhnQ3GnS2GeZPHfA=
github.com/canonical/go-dqlite v1.21.0/go.mod h1:Uvy943N8R4CFUAs59A1NVaziWY9nJ686lScY7ywurfg=
github.com/canonical/jimm-go-sdk/v3 v3.0.5 h1:eQvn35wlmv+uNfyB7FHm+SkCigBu0x2VS1FlsaNor4Q=
github.com/canonical/jimm-go-sdk/v3 v3.0.5/go.mod h1:xcJrWTpLHSw3Z16/1Zcvh31awlwIzjXdrYUYCVZhc5s=
github.com/canonical/jimm-go-sdk/v3 v3.0.6 h1:ovQAEb5R5sSl7Edn27QTi/IyCX93xd87jE9ygj14mG0=
github.com/canonical/jimm-go-sdk/v3 v3.0.6/go.mod h1:xcJrWTpLHSw3Z16/1Zcvh31awlwIzjXdrYUYCVZhc5s=
github.com/canonical/lxd v0.0.0-20231214113525-e676fc63c50a h1:Tfo/MzXK5GeG7gzSHqxGeY/669Mhh5ea43dn1mRDnk8=
github.com/canonical/lxd v0.0.0-20231214113525-e676fc63c50a/go.mod h1:UxfHGKFoRjgu1NUA9EFiR++dKvyAiT0h9HT0ffMlzjc=
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
Expand Down
4 changes: 4 additions & 0 deletions internal/juju/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ type JaasAPIClient interface {
GetGroup(req *jaasparams.GetGroupRequest) (jaasparams.GetGroupResponse, error)
RenameGroup(req *jaasparams.RenameGroupRequest) error
RemoveGroup(req *jaasparams.RemoveGroupRequest) error
AddRole(req *jaasparams.AddRoleRequest) (jaasparams.AddRoleResponse, error)
GetRole(req *jaasparams.GetRoleRequest) (jaasparams.GetRoleResponse, error)
RenameRole(req *jaasparams.RenameRoleRequest) error
RemoveRole(req *jaasparams.RemoveRoleRequest) error
}

// KubernetesCloudAPIClient defines the set of methods that the Kubernetes cloud API provides.
Expand Down
75 changes: 75 additions & 0 deletions internal/juju/jaas.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,78 @@ func (jc *jaasClient) RemoveGroup(name string) error {
req := params.RemoveGroupRequest{Name: name}
return client.RemoveGroup(&req)
}

// JaasRole represents a JAAS role used for permissions management.
type JaasRole struct {
SimoneDutto marked this conversation as resolved.
Show resolved Hide resolved
Name string
UUID string
}

// AddRole attempts to create a new role with the provided name.
func (jc *jaasClient) AddRole(name string) (string, error) {
conn, err := jc.GetConnection(nil)
if err != nil {
return "", err
}
defer func() { _ = conn.Close() }()

client := jc.getJaasApiClient(conn)
req := params.AddRoleRequest{Name: name}

resp, err := client.AddRole(&req)
if err != nil {
return "", err
}
return resp.UUID, nil
}

// ReadRoleByUUID attempts to read a role that matches the provided UUID.
func (jc *jaasClient) ReadRoleByUUID(uuid string) (*JaasRole, error) {
return jc.readRole(&params.GetRoleRequest{UUID: uuid})
}

// ReadRoleByName attempts to read a role that matches the provided name.
func (jc *jaasClient) ReadRoleByName(name string) (*JaasRole, error) {
return jc.readRole(&params.GetRoleRequest{Name: name})
}

Comment on lines +245 to +254
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two methods are implementation details. Which the caller shouldn't have to care about. It's cleaner at a package level to have a ReadRole method, then internally decide if it's a UUID or string.

func (jc *jaasClient) readRole(req *params.GetRoleRequest) (*JaasRole, error) {
conn, err := jc.GetConnection(nil)
if err != nil {
return nil, err
}
defer func() { _ = conn.Close() }()

client := jc.getJaasApiClient(conn)
resp, err := client.GetRole(req)
if err != nil {
return nil, err
}
return &JaasRole{Name: resp.Name, UUID: resp.UUID}, nil
}

// RenameRole attempts to rename a role that matches the provided name.
func (jc *jaasClient) RenameRole(name, newName string) error {
conn, err := jc.GetConnection(nil)
if err != nil {
return err
}
defer func() { _ = conn.Close() }()

client := jc.getJaasApiClient(conn)
req := params.RenameRoleRequest{Name: name, NewName: newName}
return client.RenameRole(&req)
}

// RemoveRole attempts to remove a role that matches the provided name.
func (jc *jaasClient) RemoveRole(name string) error {
conn, err := jc.GetConnection(nil)
if err != nil {
return err
}
defer func() { _ = conn.Close() }()

client := jc.getJaasApiClient(conn)
req := params.RemoveRoleRequest{Name: name}
return client.RemoveRole(&req)
}
70 changes: 70 additions & 0 deletions internal/juju/jaas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,76 @@ func (s *JaasSuite) TestRemoveGroup() {
s.Require().NoError(err)
}

func (s *JaasSuite) TestAddRole() {
defer s.setupMocks(s.T()).Finish()

name := "role"
req := &params.AddRoleRequest{Name: name}
resp := params.AddRoleResponse{Role: params.Role{UUID: "uuid", Name: name}}

s.mockJaasClient.EXPECT().AddRole(req).Return(resp, nil)

client := s.getJaasClient()
uuid, err := client.AddRole(name)
s.Require().NoError(err)
s.Require().Equal(resp.UUID, uuid)
}

func (s *JaasSuite) TestGetRole() {
defer s.setupMocks(s.T()).Finish()

uuid := "uuid"
name := "role"

req := &params.GetRoleRequest{UUID: uuid}
resp := params.GetRoleResponse{Role: params.Role{UUID: uuid, Name: name}}
s.mockJaasClient.EXPECT().GetRole(req).Return(resp, nil)

client := s.getJaasClient()
gotRole, err := client.ReadRoleByUUID(uuid)
s.Require().NoError(err)
s.Require().Equal(*gotRole, JaasRole{UUID: uuid, Name: name})
}

func (s *JaasSuite) TestGetRoleNotFound() {
defer s.setupMocks(s.T()).Finish()

uuid := "uuid"

req := &params.GetRoleRequest{UUID: uuid}
s.mockJaasClient.EXPECT().GetRole(req).Return(params.GetRoleResponse{}, errors.New("role not found"))

client := s.getJaasClient()
gotRole, err := client.ReadRoleByUUID(uuid)
s.Require().Error(err)
s.Require().Nil(gotRole)
}

func (s *JaasSuite) TestRenameRole() {
defer s.setupMocks(s.T()).Finish()

name := "name"
newName := "new-name"
req := &params.RenameRoleRequest{Name: name, NewName: newName}
s.mockJaasClient.EXPECT().RenameRole(req).Return(nil)

client := s.getJaasClient()
err := client.RenameRole(name, newName)
s.Require().NoError(err)
}

func (s *JaasSuite) TestRemoveRole() {
defer s.setupMocks(s.T()).Finish()

name := "role"
req := &params.RemoveRoleRequest{Name: name}
s.mockJaasClient.EXPECT().RemoveRole(req).Return(nil)

client := s.getJaasClient()
err := client.RemoveRole(name)
s.Require().NoError(err)
}

// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestJaasSuite(t *testing.T) {
Expand Down
Loading
Loading