Skip to content

Commit 8db8ff7

Browse files
authored
[IAM] Implement federated mapping operations (#127)
[IAM] Implement federated mapping operations Which issue this PR fixes Fixes: #128 === RUN TestFederatedMappingLifecycle --- PASS: TestFederatedMappingLifecycle (3.74s) PASS Process finished with exit code 0 Reviewed-by: None <None> Reviewed-by: Anton Kachurin <[email protected]> Reviewed-by: Anton Sidelnikov <None>
1 parent 17dbe7c commit 8db8ff7

File tree

4 files changed

+358
-0
lines changed

4 files changed

+358
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package v3
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients"
8+
"github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools"
9+
"github.com/opentelekomcloud/gophertelekomcloud/openstack/identity/v3/federation/mappings"
10+
th "github.com/opentelekomcloud/gophertelekomcloud/testhelper"
11+
)
12+
13+
func TestFederatedMappingLifecycle(t *testing.T) {
14+
if os.Getenv("OS_TENANT_ADMIN") == "" {
15+
t.Skip("Policy doesn't allow iam:identityProviders:createMapping to be performed.")
16+
}
17+
18+
client, err := clients.NewIdentityV3Client()
19+
th.AssertNoErr(t, err)
20+
21+
createOpts := mappings.CreateOpts{
22+
Rules: []mappings.RuleOpts{
23+
{
24+
Local: []mappings.LocalRuleOpts{
25+
{
26+
User: &mappings.UserOpts{
27+
Name: "{0}",
28+
},
29+
},
30+
{
31+
Groups: "[\"admin\",\"manager\"]",
32+
},
33+
},
34+
Remote: []mappings.RemoteRuleOpts{
35+
{
36+
Type: "uid",
37+
},
38+
},
39+
},
40+
},
41+
}
42+
43+
mappingName := tools.RandomString("muh", 3)
44+
45+
mapping, err := mappings.Create(client, mappingName, createOpts).Extract()
46+
th.AssertNoErr(t, err)
47+
48+
defer func() {
49+
err = mappings.Delete(client, mapping.ID).ExtractErr()
50+
th.AssertNoErr(t, err)
51+
}()
52+
53+
th.AssertEquals(t, mappingName, mapping.ID)
54+
55+
got, err := mappings.Get(client, mapping.ID).Extract()
56+
th.AssertNoErr(t, err)
57+
th.AssertDeepEquals(t, mapping, got)
58+
59+
pages, err := mappings.List(client).AllPages()
60+
th.AssertNoErr(t, err)
61+
62+
mappingList, err := mappings.ExtractMappings(pages)
63+
th.AssertNoErr(t, err)
64+
found := false
65+
for _, m := range mappingList {
66+
if m.ID == mapping.ID {
67+
found = true
68+
break
69+
}
70+
}
71+
if !found {
72+
t.Fatalf("created mapping not found in the list")
73+
}
74+
75+
updateOpts := mappings.UpdateOpts{
76+
Rules: []mappings.RuleOpts{
77+
{
78+
Local: []mappings.LocalRuleOpts{
79+
{
80+
User: &mappings.UserOpts{
81+
Name: "samltestid-{0}",
82+
},
83+
},
84+
},
85+
Remote: []mappings.RemoteRuleOpts{
86+
{
87+
Type: "uid",
88+
},
89+
},
90+
},
91+
},
92+
}
93+
updated, err := mappings.Update(client, mapping.ID, updateOpts).Extract()
94+
th.AssertNoErr(t, err)
95+
96+
got2, err := mappings.Get(client, mapping.ID).Extract()
97+
th.AssertNoErr(t, err)
98+
th.AssertDeepEquals(t, updated, got2)
99+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package mappings
2+
3+
import (
4+
golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
5+
"github.com/opentelekomcloud/gophertelekomcloud/pagination"
6+
)
7+
8+
// List enumerates the Groups to which the current token has access.
9+
func List(client *golangsdk.ServiceClient) pagination.Pager {
10+
url := listURL(client)
11+
12+
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
13+
return MappingPage{pagination.LinkedPageBase{PageResult: r}}
14+
})
15+
}
16+
17+
// Get retrieves details on a single Mapping, by ID.
18+
func Get(client *golangsdk.ServiceClient, id string) (r GetResult) {
19+
_, r.Err = client.Get(mappingURL(client, id), &r.Body, nil)
20+
return
21+
}
22+
23+
// CreateOptsBuilder allows extensions to add additional parameters to
24+
// the Create request.
25+
type CreateOptsBuilder interface {
26+
ToMappingCreateMap() (map[string]interface{}, error)
27+
}
28+
29+
// CreateOpts provides options used to create a mapping.
30+
type CreateOpts struct {
31+
// Rules used to map federated users to local users.
32+
Rules []RuleOpts `json:"rules" required:"true"`
33+
}
34+
35+
type RuleOpts struct {
36+
Local []LocalRuleOpts `json:"local" required:"true"`
37+
Remote []RemoteRuleOpts `json:"remote" required:"true"`
38+
}
39+
40+
type LocalRuleOpts struct {
41+
User *UserOpts `json:"user,omitempty"`
42+
Group *GroupOpts `json:"group,omitempty"`
43+
Groups string `json:"groups,omitempty"`
44+
}
45+
46+
type UserOpts struct {
47+
Name string `json:"name" required:"true"`
48+
}
49+
50+
type GroupOpts struct {
51+
Name string `json:"name" required:"true"`
52+
Domain *Domain `json:"domain,omitempty"`
53+
}
54+
55+
type Domain struct {
56+
Name string `json:"name,omitempty"`
57+
ID string `json:"id,omitempty"`
58+
}
59+
60+
type RemoteRuleOpts struct {
61+
Type string `json:"type" required:"true"`
62+
NotAnyOf []string `json:"not_any_of,omitempty"`
63+
AnyOneOf []string `json:"any_one_of,omitempty"`
64+
Regex *bool `json:"regex,omitempty"`
65+
}
66+
67+
// ToMappingCreateMap formats a CreateOpts into a create request.
68+
func (opts CreateOpts) ToMappingCreateMap() (map[string]interface{}, error) {
69+
b, err := golangsdk.BuildRequestBody(opts, "mapping")
70+
if err != nil {
71+
return nil, err
72+
}
73+
74+
return b, nil
75+
}
76+
77+
// Create creates a new Mapping, by ID.
78+
func Create(client *golangsdk.ServiceClient, mappingID string, opts CreateOptsBuilder) (r CreateResult) {
79+
b, err := opts.ToMappingCreateMap()
80+
if err != nil {
81+
r.Err = err
82+
return
83+
}
84+
_, r.Err = client.Put(mappingURL(client, mappingID), &b, &r.Body, &golangsdk.RequestOpts{
85+
OkCodes: []int{201},
86+
})
87+
return
88+
}
89+
90+
// UpdateOptsBuilder allows extensions to add additional parameters to
91+
// the Update request.
92+
type UpdateOptsBuilder interface {
93+
ToMappingUpdateMap() (map[string]interface{}, error)
94+
}
95+
96+
// UpdateOpts provides options for updating a mapping.
97+
type UpdateOpts struct {
98+
// Rules used to map federated users to local users.
99+
Rules []RuleOpts `json:"rules" required:"true"`
100+
}
101+
102+
// ToMappingUpdateMap formats a UpdateOpts into an update request.
103+
func (opts UpdateOpts) ToMappingUpdateMap() (map[string]interface{}, error) {
104+
b, err := golangsdk.BuildRequestBody(opts, "mapping")
105+
if err != nil {
106+
return nil, err
107+
}
108+
109+
return b, nil
110+
}
111+
112+
// Update updates an existing Mapping, by ID.
113+
func Update(client *golangsdk.ServiceClient, mappingID string, opts UpdateOptsBuilder) (r UpdateResult) {
114+
b, err := opts.ToMappingUpdateMap()
115+
if err != nil {
116+
r.Err = err
117+
return
118+
}
119+
_, r.Err = client.Patch(mappingURL(client, mappingID), &b, &r.Body, &golangsdk.RequestOpts{
120+
OkCodes: []int{200},
121+
})
122+
return
123+
}
124+
125+
// Delete deletes a mapping, by ID.
126+
func Delete(client *golangsdk.ServiceClient, mappingID string) (r DeleteResult) {
127+
_, r.Err = client.Delete(mappingURL(client, mappingID), nil)
128+
return
129+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package mappings
2+
3+
import (
4+
golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
5+
"github.com/opentelekomcloud/gophertelekomcloud/pagination"
6+
)
7+
8+
// Mapping helps manage mapping rules.
9+
type Mapping struct {
10+
// ID is the unique ID of the mapping.
11+
ID string `json:"id"`
12+
13+
// Resource Links of mappings.
14+
Links map[string]interface{} `json:"links"`
15+
16+
// Rules used to map federated users to local users
17+
Rules []Rule `json:"rules"`
18+
}
19+
20+
type Rule struct {
21+
Local []LocalRule `json:"local"`
22+
Remote []RemoteRule `json:"remote"`
23+
}
24+
25+
type LocalRule struct {
26+
User User `json:"user"`
27+
Group Group `json:"group"`
28+
}
29+
30+
type User struct {
31+
Name string `json:"name"`
32+
}
33+
34+
type Group struct {
35+
Name string `json:"name"`
36+
}
37+
38+
type RemoteRule struct {
39+
Type string `json:"type"`
40+
NotAnyOf []string `json:"not_any_of"`
41+
}
42+
43+
type mappingResult struct {
44+
golangsdk.Result
45+
}
46+
47+
// GetResult is the response from a Get operation. Call its Extract method
48+
// to interpret it as a Mapping.
49+
type GetResult struct {
50+
mappingResult
51+
}
52+
53+
// CreateResult is the response from a Create operation. Call its Extract method
54+
// to interpret it as a Mapping.
55+
type CreateResult struct {
56+
mappingResult
57+
}
58+
59+
// UpdateResult is the response from an Update operation. Call its Extract
60+
// method to interpret it as a Mapping.
61+
type UpdateResult struct {
62+
mappingResult
63+
}
64+
65+
// DeleteResult is the response from a Delete operation. Call its ExtractErr to
66+
// determine if the request succeeded or failed.
67+
type DeleteResult struct {
68+
golangsdk.ErrResult
69+
}
70+
71+
// MappingPage is a single page of Mapping results.
72+
type MappingPage struct {
73+
pagination.LinkedPageBase
74+
}
75+
76+
// IsEmpty determines whether or not a page of Mappings contains any results.
77+
func (r MappingPage) IsEmpty() (bool, error) {
78+
mappings, err := ExtractMappings(r)
79+
return len(mappings) == 0, err
80+
}
81+
82+
// NextPageURL extracts the next/previous/self links from the links section of the result.
83+
func (r MappingPage) NextPageURL() (string, error) {
84+
var s struct {
85+
Links struct {
86+
Next string `json:"next"`
87+
Previous string `json:"previous"`
88+
Self string `json:"self"`
89+
} `json:"links"`
90+
}
91+
err := r.ExtractInto(&s)
92+
if err != nil {
93+
return "", err
94+
}
95+
return s.Links.Next, err
96+
}
97+
98+
// ExtractMappings returns a slice of Mappings contained in a linked page of results.
99+
func ExtractMappings(r pagination.Page) ([]Mapping, error) {
100+
var s struct {
101+
Mappings []Mapping `json:"mappings"`
102+
}
103+
err := (r.(MappingPage)).ExtractInto(&s)
104+
return s.Mappings, err
105+
}
106+
107+
// Extract interprets any group results as a Mapping.
108+
func (r mappingResult) Extract() (*Mapping, error) {
109+
var s struct {
110+
Mapping *Mapping `json:"mapping"`
111+
}
112+
err := r.ExtractInto(&s)
113+
return s.Mapping, err
114+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package mappings
2+
3+
import (
4+
golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
5+
"github.com/opentelekomcloud/gophertelekomcloud/openstack/identity/v3/federation"
6+
)
7+
8+
const rootPath = "mappings"
9+
10+
func listURL(client *golangsdk.ServiceClient) string {
11+
return client.ServiceURL(federation.BaseURL, rootPath)
12+
}
13+
14+
func mappingURL(client *golangsdk.ServiceClient, mappingID string) string {
15+
return client.ServiceURL(federation.BaseURL, rootPath, mappingID)
16+
}

0 commit comments

Comments
 (0)