Skip to content
This repository was archived by the owner on Aug 1, 2023. It is now read-only.

Commit f928634

Browse files
committed
Merge pull request #468 from DSpeichert/role_assignments
[rfr] Keystone Identity /v3/role_assignments
2 parents 9b4df56 + 1cc1c84 commit f928634

File tree

6 files changed

+260
-0
lines changed

6 files changed

+260
-0
lines changed

openstack/identity/v3/roles/doc.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Package roles provides information and interaction with the roles API
2+
// resource for the OpenStack Identity service.
3+
package roles
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package roles
2+
3+
import (
4+
"github.com/rackspace/gophercloud"
5+
"github.com/rackspace/gophercloud/pagination"
6+
)
7+
8+
// ListAssignmentsOptsBuilder allows extensions to add additional parameters to
9+
// the ListAssignments request.
10+
type ListAssignmentsOptsBuilder interface {
11+
ToRolesListAssignmentsQuery() (string, error)
12+
}
13+
14+
// ListAssignmentsOpts allows you to query the ListAssignments method.
15+
// Specify one of or a combination of GroupId, RoleId, ScopeDomainId, ScopeProjectId,
16+
// and/or UserId to search for roles assigned to corresponding entities.
17+
// Effective lists effective assignments at the user, project, and domain level,
18+
// allowing for the effects of group membership.
19+
type ListAssignmentsOpts struct {
20+
GroupId string `q:"group.id"`
21+
RoleId string `q:"role.id"`
22+
ScopeDomainId string `q:"scope.domain.id"`
23+
ScopeProjectId string `q:"scope.project.id"`
24+
UserId string `q:"user.id"`
25+
Effective bool `q:"effective"`
26+
}
27+
28+
// ToRolesListAssignmentsQuery formats a ListAssignmentsOpts into a query string.
29+
func (opts ListAssignmentsOpts) ToRolesListAssignmentsQuery() (string, error) {
30+
q, err := gophercloud.BuildQueryString(opts)
31+
if err != nil {
32+
return "", err
33+
}
34+
return q.String(), nil
35+
}
36+
37+
// ListAssignments enumerates the roles assigned to a specified resource.
38+
func ListAssignments(client *gophercloud.ServiceClient, opts ListAssignmentsOptsBuilder) pagination.Pager {
39+
url := listAssignmentsURL(client)
40+
query, err := opts.ToRolesListAssignmentsQuery()
41+
if err != nil {
42+
return pagination.Pager{Err: err}
43+
}
44+
url += query
45+
createPage := func(r pagination.PageResult) pagination.Page {
46+
return RoleAssignmentsPage{pagination.LinkedPageBase{PageResult: r}}
47+
}
48+
49+
return pagination.NewPager(client, url, createPage)
50+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package roles
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"reflect"
7+
"testing"
8+
9+
"github.com/rackspace/gophercloud/pagination"
10+
"github.com/rackspace/gophercloud/testhelper"
11+
"github.com/rackspace/gophercloud/testhelper/client"
12+
)
13+
14+
func TestListSinglePage(t *testing.T) {
15+
testhelper.SetupHTTP()
16+
defer testhelper.TeardownHTTP()
17+
18+
testhelper.Mux.HandleFunc("/role_assignments", func(w http.ResponseWriter, r *http.Request) {
19+
testhelper.TestMethod(t, r, "GET")
20+
testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
21+
22+
w.Header().Add("Content-Type", "application/json")
23+
fmt.Fprintf(w, `
24+
{
25+
"role_assignments": [
26+
{
27+
"links": {
28+
"assignment": "http://identity:35357/v3/domains/161718/users/313233/roles/123456"
29+
},
30+
"role": {
31+
"id": "123456"
32+
},
33+
"scope": {
34+
"domain": {
35+
"id": "161718"
36+
}
37+
},
38+
"user": {
39+
"id": "313233"
40+
}
41+
},
42+
{
43+
"links": {
44+
"assignment": "http://identity:35357/v3/projects/456789/groups/101112/roles/123456",
45+
"membership": "http://identity:35357/v3/groups/101112/users/313233"
46+
},
47+
"role": {
48+
"id": "123456"
49+
},
50+
"scope": {
51+
"project": {
52+
"id": "456789"
53+
}
54+
},
55+
"user": {
56+
"id": "313233"
57+
}
58+
}
59+
],
60+
"links": {
61+
"self": "http://identity:35357/v3/role_assignments?effective",
62+
"previous": null,
63+
"next": null
64+
}
65+
}
66+
`)
67+
})
68+
69+
count := 0
70+
err := ListAssignments(client.ServiceClient(), ListAssignmentsOpts{}).EachPage(func(page pagination.Page) (bool, error) {
71+
count++
72+
actual, err := ExtractRoleAssignments(page)
73+
if err != nil {
74+
return false, err
75+
}
76+
77+
expected := []RoleAssignment{
78+
RoleAssignment{
79+
Role: Role{ID: "123456"},
80+
Scope: Scope{Domain: Domain{ID: "161718"}},
81+
User: User{ID: "313233"},
82+
Group: Group{},
83+
},
84+
RoleAssignment{
85+
Role: Role{ID: "123456"},
86+
Scope: Scope{Project: Project{ID: "456789"}},
87+
User: User{ID: "313233"},
88+
Group: Group{},
89+
},
90+
}
91+
92+
if !reflect.DeepEqual(expected, actual) {
93+
t.Errorf("Expected %#v, got %#v", expected, actual)
94+
}
95+
96+
return true, nil
97+
})
98+
if err != nil {
99+
t.Errorf("Unexpected error while paging: %v", err)
100+
}
101+
if count != 1 {
102+
t.Errorf("Expected 1 page, got %d", count)
103+
}
104+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package roles
2+
3+
import (
4+
"github.com/rackspace/gophercloud/pagination"
5+
6+
"github.com/mitchellh/mapstructure"
7+
)
8+
9+
// RoleAssignment is the result of a role assignments query.
10+
type RoleAssignment struct {
11+
Role Role `json:"role,omitempty"`
12+
Scope Scope `json:"scope,omitempty"`
13+
User User `json:"user,omitempty"`
14+
Group Group `json:"group,omitempty"`
15+
}
16+
17+
type Role struct {
18+
ID string `json:"id,omitempty"`
19+
}
20+
21+
type Scope struct {
22+
Domain Domain `json:"domain,omitempty"`
23+
Project Project `json:"domain,omitempty"`
24+
}
25+
26+
type Domain struct {
27+
ID string `json:"id,omitempty"`
28+
}
29+
30+
type Project struct {
31+
ID string `json:"id,omitempty"`
32+
}
33+
34+
type User struct {
35+
ID string `json:"id,omitempty"`
36+
}
37+
38+
type Group struct {
39+
ID string `json:"id,omitempty"`
40+
}
41+
42+
// RoleAssignmentsPage is a single page of RoleAssignments results.
43+
type RoleAssignmentsPage struct {
44+
pagination.LinkedPageBase
45+
}
46+
47+
// IsEmpty returns true if the page contains no results.
48+
func (p RoleAssignmentsPage) IsEmpty() (bool, error) {
49+
roleAssignments, err := ExtractRoleAssignments(p)
50+
if err != nil {
51+
return true, err
52+
}
53+
return len(roleAssignments) == 0, nil
54+
}
55+
56+
// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
57+
func (page RoleAssignmentsPage) NextPageURL() (string, error) {
58+
type resp struct {
59+
Links struct {
60+
Next string `mapstructure:"next"`
61+
} `mapstructure:"links"`
62+
}
63+
64+
var r resp
65+
err := mapstructure.Decode(page.Body, &r)
66+
if err != nil {
67+
return "", err
68+
}
69+
70+
return r.Links.Next, nil
71+
}
72+
73+
// ExtractRoleAssignments extracts a slice of RoleAssignments from a Collection acquired from List.
74+
func ExtractRoleAssignments(page pagination.Page) ([]RoleAssignment, error) {
75+
var response struct {
76+
RoleAssignments []RoleAssignment `mapstructure:"role_assignments"`
77+
}
78+
79+
err := mapstructure.Decode(page.(RoleAssignmentsPage).Body, &response)
80+
return response.RoleAssignments, err
81+
}

openstack/identity/v3/roles/urls.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package roles
2+
3+
import "github.com/rackspace/gophercloud"
4+
5+
func listAssignmentsURL(client *gophercloud.ServiceClient) string {
6+
return client.ServiceURL("role_assignments")
7+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package roles
2+
3+
import (
4+
"testing"
5+
6+
"github.com/rackspace/gophercloud"
7+
)
8+
9+
func TestListAssignmentsURL(t *testing.T) {
10+
client := gophercloud.ServiceClient{Endpoint: "http://localhost:5000/v3/"}
11+
url := listAssignmentsURL(&client)
12+
if url != "http://localhost:5000/v3/role_assignments" {
13+
t.Errorf("Unexpected list URL generated: [%s]", url)
14+
}
15+
}

0 commit comments

Comments
 (0)