Skip to content
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Temporary edit files
*.swp
*~
59 changes: 55 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,10 @@ func main() {
ckReq := client.NewCkRequest()

// Allow GET method on /me
ckReq.AddRule("GET", "/me")
ckReq.AddRules(ovh.ReadOnly, "/me")

// Allow GET method on /xdsl and all its sub routes
ckReq.AddRule("GET", "/xdsl/*")
ckReq.AddRecursiveRulesRules(ovh.ReadOnly, "/xdsl")

// Run the request
response, err := ckReq.Do()
Expand Down Expand Up @@ -330,18 +330,69 @@ While this is simple and may be managed directly with the API as-is, this can be
and we recommend using the ``CkRequest`` helper. It basically manages the list of authorizations
for you and the actual request.

*example*: Grant on all /sms and identity
```go
client, err := ovh.NewEndpointClient("ovh-eu")
if err == nil {
// Do something
}
req := client.NewCkRequest()
req.AddRules(ovh.ReadOnly, "/me")
req.AddRecursiveRulesRules(ovh.ReadWrite, "/sms")
pendingCk, err := req.Do()
```

This example will generate a request for:

- GET /me
- GET /sms
- GET /sms/*
- POST /sms
- POST /sms/*
- PUT /sms
- PUT /sms/*
- DELETE /sms
- DELETE /sms/*

Which would be tedious to do by hand...

*Create a ``CkRequest``*:

```go
req := client.NewCkRequest()
```

*Add a rule*:

*Request access on a specific path and method* (advanced):
```go
// Use this method for fine-grain access control. In most case, you'll
// want to use the methods below.
req.AddRule("VERB", "PATTERN")
```

*Request access on specific path*:
```go
// This will generate all patterns for GET PATH
req.AddRules(ovh.ReadOnly, "/PATH")

// This will generate all patterns for PATH for all HTTP verbs
req.AddRules(ovh.ReadWrite, "/PATH")

// This will generate all patterns for PATH for all HTTP verbs, except DELETE
req.AddRules(ovh.ReadWriteSafe, "/PATH")
```

*Request access on path and all sub-path*:
```go
// This will generate all patterns for GET PATH
req.AddRecursiveRules(ovh.ReadOnly, "/PATH")

// This will generate all patterns for PATH for all HTTP verbs
req.AddRecursiveRules(ovh.ReadWrite, "/PATH")

// This will generate all patterns for PATH for all HTTP verbs, except DELETE
req.AddRecusriveRules(ovh.ReadWriteSafe, "/PATH")
```

*Create key*:

```go
Expand Down
37 changes: 34 additions & 3 deletions ovh/consumer_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ package ovh

import (
"fmt"
"strings"
)

// Map user friendly access level names to corresponding HTTP verbs
var (
ReadOnly = []string{"GET"}
ReadWrite = []string{"GET", "POST", "PUT", "DELETE"}
ReadWriteSafe = []string{"GET", "POST", "PUT"}
)

// AccessRule represents a method allowed for a path
Expand Down Expand Up @@ -31,7 +39,7 @@ type CkValidationState struct {
// consumerKey.
type CkRequest struct {
client *Client
AccessRules []*AccessRule `json:"accessRules"`
AccessRules []AccessRule `json:"accessRules"`
}

func (ck *CkValidationState) String() string {
Expand All @@ -46,18 +54,41 @@ func (ck *CkValidationState) String() string {
func (c *Client) NewCkRequest() *CkRequest {
return &CkRequest{
client: c,
AccessRules: []*AccessRule{},
AccessRules: []AccessRule{},
}
}

// AddRule adds a new rule to the ckRequest
func (ck *CkRequest) AddRule(method, path string) {
ck.AccessRules = append(ck.AccessRules, &AccessRule{
ck.AccessRules = append(ck.AccessRules, AccessRule{
Method: method,
Path: path,
})
}

// AddRules adds grant requests on "path" for all methods. "ReadOnly",
// "ReadWrite" and "ReadWriteSafe" should be used for "methods" unless
// specific access are required.
func (ck *CkRequest) AddRules(methods []string, path string) {
for _, method := range methods {
ck.AddRule(method, path)
}

}

// AddRecursiveRules adds grant requests on "path" and "path/*", for all
// methods "ReadOnly", "ReadWrite" and "ReadWriteSafe" should be used for
// "methods" unless specific access are required.
func (ck *CkRequest) AddRecursiveRules(methods []string, path string) {
path = strings.TrimRight(path, "/")

// Add rules. Skip base rule when requesting access to "/"
if path != "" {
ck.AddRules(methods, path)
}
ck.AddRules(methods, path+"/*")
}

// Do executes the request. On success, set the consumer key in the client
// and return the URL the user needs to visit to validate the key
func (ck *CkRequest) Do() (*CkValidationState, error) {
Expand Down
49 changes: 49 additions & 0 deletions ovh/consumer_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ovh
import (
"fmt"
"net/http"
"reflect"
"testing"
)

Expand Down Expand Up @@ -80,6 +81,54 @@ func TestInvalidCkReqest(t *testing.T) {
}
}

func TestAddRules(t *testing.T) {
// Init test
var InputRequest *http.Request
var InputRequestBody string
ts, client := initMockServer(&InputRequest, http.StatusForbidden, `{"message":"Invalid application key"}`, &InputRequestBody)
client.ConsumerKey = ""
defer ts.Close()

// Test: allow all
ckRequest := client.NewCkRequest()
ckRequest.AddRecursiveRules(ReadWrite, "/")
ExpectedRules := []AccessRule{
AccessRule{Method: "GET", Path: "/*"},
AccessRule{Method: "POST", Path: "/*"},
AccessRule{Method: "PUT", Path: "/*"},
AccessRule{Method: "DELETE", Path: "/*"},
}
if !reflect.DeepEqual(ckRequest.AccessRules, ExpectedRules) {
t.Fatalf("Inserting recursive RW rules for / should generate %v. Got %v", ExpectedRules, ckRequest.AccessRules)
}

// Test: allow exactly /sms, RO
ckRequest = client.NewCkRequest()
ckRequest.AddRules(ReadOnly, "/sms")
ExpectedRules = []AccessRule{
AccessRule{Method: "GET", Path: "/sms"},
}
if !reflect.DeepEqual(ckRequest.AccessRules, ExpectedRules) {
t.Fatalf("Inserting RO rule for /sms should generate %v. Got %v", ExpectedRules, ckRequest.AccessRules)
}

// Test: allow /sms/*, RW, no delete
ckRequest = client.NewCkRequest()
ckRequest.AddRecursiveRules(ReadWriteSafe, "/sms")
ExpectedRules = []AccessRule{
AccessRule{Method: "GET", Path: "/sms"},
AccessRule{Method: "POST", Path: "/sms"},
AccessRule{Method: "PUT", Path: "/sms"},

AccessRule{Method: "GET", Path: "/sms/*"},
AccessRule{Method: "POST", Path: "/sms/*"},
AccessRule{Method: "PUT", Path: "/sms/*"},
}
if !reflect.DeepEqual(ckRequest.AccessRules, ExpectedRules) {
t.Fatalf("Inserting recursive safe RW rule for /sms should generate %v. Got %v", ExpectedRules, ckRequest.AccessRules)
}
}

func TestCkReqestString(t *testing.T) {
ckValidationState := &CkValidationState{
ConsumerKey: "ck",
Expand Down