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

Identity V3: add Trust Delete function #1644

Merged
merged 2 commits into from
Jul 16, 2019
Merged
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
32 changes: 32 additions & 0 deletions openstack/identity/v3/extensions/trusts/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,37 @@ Example to Create a Token with Username, Password, and Trust ID
if err != nil {
panic(err)
}

Example to Create a Trust

expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 999999999, time.UTC)
createOpts := trusts.CreateOpts{
ExpiresAt: &expiresAt,
Impersonation: true,
AllowRedelegation: true,
ProjectID: "9b71012f5a4a4aef9193f1995fe159b2",
Roles: []trusts.Role{
{
Name: "member",
},
},
TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf",
TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3",
}

trust, err := trusts.Create(identityClient, createOpts).Extract()
if err != nil {
panic(err)
}

fmt.Printf("Trust: %+v\n", trust)

Example to Delete a Trust

trustID := "3422b7c113894f5d90665e1a79655e23"
err := trusts.Delete(identityClient, trustID).ExtractErr()
if err != nil {
panic(err)
}
*/
package trusts
80 changes: 79 additions & 1 deletion openstack/identity/v3/extensions/trusts/requests.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package trusts

import "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
import (
"time"

"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
)

// AuthOptsExt extends the base Identity v3 tokens AuthOpts with a TrustID.
type AuthOptsExt struct {
Expand Down Expand Up @@ -37,3 +42,76 @@ func (opts AuthOptsExt) ToTokenV3ScopeMap() (map[string]interface{}, error) {
func (opts AuthOptsExt) CanReauth() bool {
return opts.AuthOptionsBuilder.CanReauth()
}

// CreateOptsBuilder allows extensions to add additional parameters to
// the Create request.
type CreateOptsBuilder interface {
ToTrustCreateMap() (map[string]interface{}, error)
}

// CreateOpts provides options used to create a new trust.
type CreateOpts struct {
// Impersonation allows the trustee to impersonate the trustor.
Impersonation bool `json:"impersonation" required:"true"`

// TrusteeUserID is a user who is capable of consuming the trust.
TrusteeUserID string `json:"trustee_user_id" required:"true"`

// TrustorUserID is a user who created the trust.
TrustorUserID string `json:"trustor_user_id" required:"true"`

// AllowRedelegation enables redelegation of a trust.
AllowRedelegation bool `json:"allow_redelegation,omitempty"`

// ExpiresAt sets expiration time on trust.
ExpiresAt *time.Time `json:"-"`

// ProjectID identifies the project.
ProjectID string `json:"project_id,omitempty"`

// RedelegationCount specifies a depth of the redelegation chain.
RedelegationCount int `json:"redelegation_count,omitempty"`

// RemainingUses specifies how many times a trust can be used to get a token.
RemainingUses int `json:"remaining_uses,omitempty"`

// Roles specifies roles that need to be granted to trustee.
Roles []Role `json:"roles,omitempty"`
}

// ToTrustCreateMap formats a CreateOpts into a create request.
func (opts CreateOpts) ToTrustCreateMap() (map[string]interface{}, error) {
parent := "trust"

b, err := gophercloud.BuildRequestBody(opts, parent)
if err != nil {
return nil, err
}

if opts.ExpiresAt != nil {
if v, ok := b[parent].(map[string]interface{}); ok {
v["expires_at"] = opts.ExpiresAt.Format(gophercloud.RFC3339Milli)
}
}

return b, nil
}

// Create creates a new Trust.
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
b, err := opts.ToTrustCreateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{201},
})
return
}

// Delete deletes a trust.
func Delete(client *gophercloud.ServiceClient, trustID string) (r DeleteResult) {
_, r.Err = client.Delete(deleteURL(client, trustID), nil)
return
}
33 changes: 33 additions & 0 deletions openstack/identity/v3/extensions/trusts/results.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
package trusts

import "github.com/gophercloud/gophercloud"

type trustResult struct {
gophercloud.Result
}

// CreateResult is the response from a Create operation. Call its Extract method
// to interpret it as a Trust.
type CreateResult struct {
trustResult
}

// DeleteResult is the response from a Delete operation. Call its ExtractErr to
// determine if the request succeeded or failed.
type DeleteResult struct {
gophercloud.ErrResult
}

// Extract interprets any trust result as a Trust.
func (r trustResult) Extract() (*Trust, error) {
var s struct {
Trust *Trust `json:"trust"`
}
err := r.ExtractInto(&s)
return s.Trust, err
}

// TrusteeUser represents the trusted user ID of a trust.
type TrusteeUser struct {
ID string `json:"id"`
Expand All @@ -21,6 +48,12 @@ type Trust struct {
RedelegationCount int `json:"redelegation_count"`
}

// Role specifies a single role that is granted to a trustee.
type Role struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
}

// TokenExt represents an extension of the base token result.
type TokenExt struct {
Trust Trust `json:"OS-TRUST:trust"`
Expand Down
68 changes: 68 additions & 0 deletions openstack/identity/v3/extensions/trusts/testing/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,51 @@ import (

"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
"github.com/gophercloud/gophercloud/testhelper"
"github.com/gophercloud/gophercloud/testhelper/client"
)

const CreateRequest = `
{
"trust": {
"expires_at": "2019-12-01T14:00:00.999999Z",
"impersonation": true,
"allow_redelegation": true,
"project_id": "9b71012f5a4a4aef9193f1995fe159b2",
"roles": [
{
"name": "member"
}
],
"trustee_user_id": "ecb37e88cc86431c99d0332208cb6fbf",
"trustor_user_id": "959ed913a32c4ec88c041c98e61cbbc3"
}
}
`

const CreateResponse = `
{
"trust": {
"expires_at": "2019-12-01T14:00:00.999999Z",
"id": "3422b7c113894f5d90665e1a79655e23",
"impersonation": true,
"redelegation_count": 10,
"project_id": "9b71012f5a4a4aef9193f1995fe159b2",
"remaining_uses": null,
"roles": [
{
"id": "b627fca5-beb0-471a-9857-0e852b719e76",
"links": {
"self": "http://example.com/identity/v3/roles/b627fca5-beb0-471a-9857-0e852b719e76"
},
"name": "member"
}
],
"trustee_user_id": "ecb37e88cc86431c99d0332208cb6fbf",
"trustor_user_id": "959ed913a32c4ec88c041c98e61cbbc3"
}
}
`

// HandleCreateTokenWithTrustID verifies that providing certain AuthOptions and Scope results in an expected JSON structure.
func HandleCreateTokenWithTrustID(t *testing.T, options tokens.AuthOptionsBuilder, requestJSON string) {
testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -65,3 +108,28 @@ func HandleCreateTokenWithTrustID(t *testing.T, options tokens.AuthOptionsBuilde
}`)
})
}

// HandleCreateTrust creates an HTTP handler at `/OS-TRUST/trusts` on the
// test handler mux that tests trust creation.
func HandleCreateTrust(t *testing.T) {
testhelper.Mux.HandleFunc("/OS-TRUST/trusts", func(w http.ResponseWriter, r *http.Request) {
testhelper.TestMethod(t, r, "POST")
testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
testhelper.TestJSONRequest(t, r, CreateRequest)

w.WriteHeader(http.StatusCreated)
_, err := fmt.Fprintf(w, CreateResponse)
testhelper.AssertNoErr(t, err)
})
}

// HandleDeleteUserSuccessfully creates an HTTP handler at `/users` on the
// test handler mux that tests user deletion.
func HandleDeleteTrust(t *testing.T) {
testhelper.Mux.HandleFunc("/OS-TRUST/trusts/3422b7c113894f5d90665e1a79655e23", func(w http.ResponseWriter, r *http.Request) {
testhelper.TestMethod(t, r, "DELETE")
testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)

w.WriteHeader(http.StatusNoContent)
})
}
35 changes: 35 additions & 0 deletions openstack/identity/v3/extensions/trusts/testing/requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,38 @@ func TestCreateUserIDPasswordTrustID(t *testing.T) {

th.AssertDeepEquals(t, expected, actual)
}

func TestCreateTrust(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleCreateTrust(t)

expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 999999999, time.UTC)
result, err := trusts.Create(client.ServiceClient(), trusts.CreateOpts{
ExpiresAt: &expiresAt,
Impersonation: true,
AllowRedelegation: true,
ProjectID: "9b71012f5a4a4aef9193f1995fe159b2",
Roles: []trusts.Role{
{
Name: "member",
},
},
TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf",
TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3",
}).Extract()
th.AssertNoErr(t, err)

th.AssertEquals(t, "3422b7c113894f5d90665e1a79655e23", result.ID)
th.AssertEquals(t, true, result.Impersonation)
th.AssertEquals(t, 10, result.RedelegationCount)
}

func TestDeleteTrust(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleDeleteTrust(t)

res := trusts.Delete(client.ServiceClient(), "3422b7c113894f5d90665e1a79655e23")
th.AssertNoErr(t, res.Err)
}
21 changes: 21 additions & 0 deletions openstack/identity/v3/extensions/trusts/urls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package trusts

import "github.com/gophercloud/gophercloud"

const resourcePath = "OS-TRUST/trusts"

func rootURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(resourcePath)
}

func resourceURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(resourcePath, id)
}

func createURL(c *gophercloud.ServiceClient) string {
return rootURL(c)
}

func deleteURL(c *gophercloud.ServiceClient, id string) string {
return resourceURL(c, id)
}