del(rbac): removed pdp implementation, closes #90
This commit is contained in:
parent
6934917da0
commit
d934513dbd
@ -6,15 +6,18 @@ import (
|
||||
|
||||
context "github.com/SentimensRG/ctx"
|
||||
"github.com/SentimensRG/ctx/sigctx"
|
||||
"github.com/namsral/flag"
|
||||
|
||||
sub "github.com/crusttech/crust/crm"
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
"github.com/crusttech/crust/internal/rbac"
|
||||
"github.com/crusttech/crust/internal/subscription"
|
||||
"github.com/crusttech/crust/internal/version"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// log to stdout not stderr
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
log.Printf("Starting "+os.Args[0]+", version: %v, built on: %v", version.Version, version.BuildTime)
|
||||
|
||||
ctx := context.AsContext(sigctx.New())
|
||||
@ -23,14 +26,9 @@ func main() {
|
||||
"crm",
|
||||
sub.Flags,
|
||||
auth.Flags,
|
||||
rbac.Flags,
|
||||
subscription.Flags,
|
||||
)
|
||||
|
||||
// log to stdout not stderr
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
if err := sub.Init(); err != nil {
|
||||
log.Fatalf("Error initializing: %+v", err)
|
||||
}
|
||||
@ -42,6 +40,7 @@ func main() {
|
||||
|
||||
switch command {
|
||||
case "help":
|
||||
flag.PrintDefaults()
|
||||
default:
|
||||
// Checks subscription, will os.Exit(1) if there is an error
|
||||
// Disabled for now, system service is the only one that validates subscription
|
||||
|
||||
@ -22,7 +22,6 @@ import (
|
||||
"github.com/crusttech/crust/internal/config"
|
||||
"github.com/crusttech/crust/internal/metrics"
|
||||
"github.com/crusttech/crust/internal/middleware"
|
||||
"github.com/crusttech/crust/internal/rbac"
|
||||
"github.com/crusttech/crust/internal/routes"
|
||||
"github.com/crusttech/crust/internal/subscription"
|
||||
"github.com/crusttech/crust/internal/version"
|
||||
@ -62,7 +61,6 @@ func main() {
|
||||
system.Flags("system")
|
||||
|
||||
auth.Flags()
|
||||
rbac.Flags()
|
||||
subscription.Flags()
|
||||
|
||||
flag.Parse()
|
||||
|
||||
@ -6,15 +6,18 @@ import (
|
||||
|
||||
context "github.com/SentimensRG/ctx"
|
||||
"github.com/SentimensRG/ctx/sigctx"
|
||||
"github.com/namsral/flag"
|
||||
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
"github.com/crusttech/crust/internal/rbac"
|
||||
"github.com/crusttech/crust/internal/subscription"
|
||||
"github.com/crusttech/crust/internal/version"
|
||||
sub "github.com/crusttech/crust/messaging"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// log to stdout not stderr
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
log.Printf("Starting "+os.Args[0]+", version: %v, built on: %v", version.Version, version.BuildTime)
|
||||
|
||||
ctx := context.AsContext(sigctx.New())
|
||||
@ -23,14 +26,9 @@ func main() {
|
||||
"messaging",
|
||||
sub.Flags,
|
||||
auth.Flags,
|
||||
rbac.Flags,
|
||||
subscription.Flags,
|
||||
)
|
||||
|
||||
// log to stdout not stderr
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
if err := sub.Init(); err != nil {
|
||||
log.Fatalf("Error initializing: %+v", err)
|
||||
}
|
||||
@ -42,7 +40,7 @@ func main() {
|
||||
|
||||
switch command {
|
||||
case "help":
|
||||
case "merge-users":
|
||||
flag.PrintDefaults()
|
||||
default:
|
||||
// Checks subscription, will os.Exit(1) if there is an error
|
||||
// Disabled for now, system service is the only one that validates subscription
|
||||
|
||||
@ -8,16 +8,15 @@ import (
|
||||
systemService "github.com/crusttech/crust/system/service"
|
||||
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
"github.com/crusttech/crust/internal/rbac"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flags("system", service.Flags, auth.Flags, rbac.Flags)
|
||||
|
||||
// log to stdout not stderr
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
flags("system", service.Flags, auth.Flags)
|
||||
|
||||
service.InitDatabase()
|
||||
systemService.Init()
|
||||
|
||||
|
||||
@ -6,15 +6,18 @@ import (
|
||||
|
||||
context "github.com/SentimensRG/ctx"
|
||||
"github.com/SentimensRG/ctx/sigctx"
|
||||
"github.com/namsral/flag"
|
||||
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
"github.com/crusttech/crust/internal/rbac"
|
||||
"github.com/crusttech/crust/internal/subscription"
|
||||
"github.com/crusttech/crust/internal/version"
|
||||
sub "github.com/crusttech/crust/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// log to stdout not stderr
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
log.Printf("Starting "+os.Args[0]+", version: %v, built on: %v", version.Version, version.BuildTime)
|
||||
|
||||
ctx := context.AsContext(sigctx.New())
|
||||
@ -23,14 +26,9 @@ func main() {
|
||||
"system",
|
||||
sub.Flags,
|
||||
auth.Flags,
|
||||
rbac.Flags,
|
||||
subscription.Flags,
|
||||
)
|
||||
|
||||
// log to stdout not stderr
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
if err := sub.Init(); err != nil {
|
||||
log.Fatalf("Error initializing: %+v", err)
|
||||
}
|
||||
@ -42,6 +40,7 @@ func main() {
|
||||
|
||||
switch command {
|
||||
case "help":
|
||||
flag.PrintDefaults()
|
||||
default:
|
||||
// Checks subscription, will os.Exit(1) if there is an error
|
||||
ctx = subscription.Monitor(ctx)
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/namsral/flag"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type (
|
||||
RBAC struct {
|
||||
Auth string
|
||||
Tenant string
|
||||
BaseURL string
|
||||
Timeout int
|
||||
}
|
||||
)
|
||||
|
||||
var rbac *RBAC
|
||||
|
||||
func (c *RBAC) Validate() error {
|
||||
if c.Auth == "" {
|
||||
return errors.New("No authentication provided for RBAC")
|
||||
}
|
||||
if c.Tenant == "" {
|
||||
return errors.New("No tenant provided for RBAC")
|
||||
}
|
||||
if c.BaseURL == "" {
|
||||
return errors.New("No Base URL provided for RBAC")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*RBAC) Init(prefix ...string) *RBAC {
|
||||
if rbac != nil {
|
||||
return rbac
|
||||
}
|
||||
rbac = new(RBAC)
|
||||
flag.StringVar(&rbac.Auth, "rbac-auth", "username:password", "Credentials to use for RBAC queries")
|
||||
flag.StringVar(&rbac.Tenant, "rbac-tenant", "", "Tenant ID")
|
||||
flag.StringVar(&rbac.BaseURL, "rbac-base-url", "", "RBAC Base URL")
|
||||
flag.IntVar(&rbac.Timeout, "rbac-timeout", 30, "RBAC request timeout (seconds)")
|
||||
return rbac
|
||||
}
|
||||
@ -1,162 +0,0 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/crusttech/crust/internal/config"
|
||||
)
|
||||
|
||||
var _ = tls.Config{}
|
||||
|
||||
type (
|
||||
Client struct {
|
||||
Transport *http.Transport
|
||||
Client *http.Client
|
||||
|
||||
debugLevel string
|
||||
config *config.RBAC
|
||||
}
|
||||
|
||||
ClientInterface interface {
|
||||
Users() *Users
|
||||
Roles() *Roles
|
||||
Resources() *Resources
|
||||
Sessions() *Sessions
|
||||
}
|
||||
)
|
||||
|
||||
func (c *Client) Users() *Users { return &Users{c} }
|
||||
func (c *Client) Roles() *Roles { return &Roles{c} }
|
||||
func (c *Client) Resources() *Resources { return &Resources{c} }
|
||||
func (c *Client) Sessions() *Sessions { return &Sessions{c} }
|
||||
|
||||
var _ ClientInterface = &Client{}
|
||||
|
||||
func New() (*Client, error) {
|
||||
if err := flags.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeout := time.Duration(flags.Timeout) * time.Second
|
||||
|
||||
transport := &http.Transport{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: timeout,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: timeout,
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: timeout,
|
||||
Transport: transport,
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
|
||||
return &Client{
|
||||
Transport: transport,
|
||||
Client: client,
|
||||
config: flags,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) Debug(debugLevel string) *Client {
|
||||
c.debugLevel = debugLevel
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) Get(url string) (*http.Response, error) {
|
||||
return c.Request("GET", url, nil)
|
||||
}
|
||||
|
||||
func (c *Client) Post(url string, body interface{}) (*http.Response, error) {
|
||||
return c.Request("POST", url, body)
|
||||
}
|
||||
|
||||
func (c *Client) Patch(url string, body interface{}) (*http.Response, error) {
|
||||
return c.Request("PATCH", url, body)
|
||||
}
|
||||
|
||||
func (c *Client) Delete(url string) (*http.Response, error) {
|
||||
return c.Request("DELETE", url, nil)
|
||||
}
|
||||
|
||||
func (c *Client) Request(method, url string, body interface{}) (*http.Response, error) {
|
||||
link := strings.TrimRight(c.config.BaseURL, "/") + "/" + strings.TrimLeft(url, "/")
|
||||
|
||||
if c.debugLevel == "info" {
|
||||
fmt.Println("RBAC >>>", method, link)
|
||||
}
|
||||
|
||||
request := func() (*http.Request, error) {
|
||||
if body != nil {
|
||||
b, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return http.NewRequest(method, link, bytes.NewBuffer(b))
|
||||
}
|
||||
return http.NewRequest(method, link, nil)
|
||||
}
|
||||
|
||||
req, err := request()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(c.config.Auth)))
|
||||
req.Header.Add("X-Tenant-Id", c.config.Tenant)
|
||||
|
||||
if c.debugLevel == "debug" {
|
||||
fmt.Println("RBAC >>> (request)")
|
||||
b, err := httputil.DumpRequestOut(req, true)
|
||||
if err != nil {
|
||||
fmt.Println("RBAC >>> Error:", err)
|
||||
} else {
|
||||
if b != nil {
|
||||
fmt.Println(strings.TrimSpace(string(b)))
|
||||
}
|
||||
}
|
||||
fmt.Println("---")
|
||||
}
|
||||
|
||||
resp, err := c.Client.Do(req)
|
||||
if c.debugLevel == "debug" {
|
||||
fmt.Println("RBAC <<< (response)")
|
||||
if err != nil {
|
||||
fmt.Println("RBAC <<< Error:", err)
|
||||
} else {
|
||||
|
||||
b, err := httputil.DumpResponse(resp, true)
|
||||
if err != nil {
|
||||
fmt.Println("RBAC <<< Error:", err)
|
||||
} else {
|
||||
if b != nil {
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println("-----------------")
|
||||
}
|
||||
if err != nil {
|
||||
if c.debugLevel == "info" {
|
||||
fmt.Println("RBAC <<< Response error", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if c.debugLevel == "info" {
|
||||
fmt.Println("RBAC <<< Response", resp.StatusCode)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/crusttech/crust/internal/config"
|
||||
)
|
||||
|
||||
var flags *config.RBAC
|
||||
|
||||
func Flags(prefix ...string) {
|
||||
flags = new(config.RBAC).Init(prefix...)
|
||||
}
|
||||
|
||||
func Debug() {
|
||||
fmt.Printf("Debug RBAC flags: %#v", *flags)
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
package rbac
|
||||
|
||||
type (
|
||||
// Permissions is a stateful object
|
||||
Permissions interface /* for Session, User, Roles, Resource */ {
|
||||
// Scoped for [Resource]
|
||||
Grant(permission string) error
|
||||
Revoke(permission string) error
|
||||
List() ([]string, error)
|
||||
|
||||
// Check permission of stateful object (Session, User, Roles)
|
||||
CheckAccess(permission string) (bool, error)
|
||||
}
|
||||
|
||||
// Roles is a stateful object
|
||||
Roles interface /* for Session, User */ {
|
||||
// Scoped to User
|
||||
Add(role string) error
|
||||
Delete(role string) error
|
||||
|
||||
// Scoped to Session, User
|
||||
List() ([]string, error)
|
||||
ListAuthorized() ([]string, error)
|
||||
|
||||
// Scoped to Session
|
||||
GrantRole(role string) error
|
||||
RevokeRole(role string) error
|
||||
|
||||
// Permissions are scoped to [Session, User]
|
||||
Permissions(role string) Permissions
|
||||
}
|
||||
|
||||
// Session object holds session state (Create, Load)
|
||||
Session interface {
|
||||
// Unscoped functions
|
||||
Create(userID string, roles ...string) error
|
||||
Load(sessionID string) error
|
||||
Delete() error
|
||||
|
||||
// User returns User scoped object with global roles/permissions
|
||||
User() (User, error)
|
||||
|
||||
// Roles and Permissions return session scoped objects
|
||||
Roles() Roles
|
||||
Permissions() Permissions
|
||||
}
|
||||
|
||||
// Resource is a static object
|
||||
Resource interface {
|
||||
Load(resource string) error
|
||||
Create(resource string) error
|
||||
Delete(resource string) error
|
||||
|
||||
RolePermissions(resource string, role string) Permissions
|
||||
UserPermissions(resource string, user string) Permissions
|
||||
}
|
||||
|
||||
// Users is a static object
|
||||
User interface {
|
||||
Load(user string) error
|
||||
Create(user string) error
|
||||
Delete(user string) error
|
||||
|
||||
// Roles and Permissions return User scoped objects
|
||||
Roles(user string) Roles
|
||||
Permissions(user string) Permissions
|
||||
}
|
||||
)
|
||||
@ -1,48 +0,0 @@
|
||||
package rbac_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/namsral/flag"
|
||||
|
||||
"github.com/crusttech/crust/internal/rbac"
|
||||
)
|
||||
|
||||
var loaded bool
|
||||
|
||||
func getClient() (*rbac.Client, error) {
|
||||
if !loaded {
|
||||
godotenv.Load("../../.env")
|
||||
|
||||
rbac.Flags()
|
||||
flag.Parse()
|
||||
loaded = true
|
||||
}
|
||||
rbac.Debug()
|
||||
client, err := rbac.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.Debug("debug")
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func assert(t *testing.T, ok bool, format string, args ...interface{}) bool {
|
||||
if !ok {
|
||||
t.Fatalf(format, args...)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func must(t *testing.T, err error, message ...string) {
|
||||
if len(message) > 0 {
|
||||
assert(t, err == nil, message[0]+": %+v", err)
|
||||
return
|
||||
}
|
||||
assert(t, err == nil, "Error: %+v", err)
|
||||
}
|
||||
|
||||
func mustFail(t *testing.T, err error) {
|
||||
assert(t, err != nil, "Expected error, got nil")
|
||||
}
|
||||
@ -1,172 +0,0 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/crusttech/crust/internal/rbac/types"
|
||||
)
|
||||
|
||||
type (
|
||||
Resources struct {
|
||||
*Client
|
||||
}
|
||||
|
||||
ResourcesRole struct {
|
||||
Rolepath string `json:"role"`
|
||||
Operation string `json:"operation"`
|
||||
}
|
||||
|
||||
ResourcesInterface interface {
|
||||
Create(resourceID string, operations []string) error
|
||||
Get(resourceID string) (*types.Resource, error)
|
||||
Delete(resourceID string, resourceIDs ...string) error
|
||||
|
||||
Grant(resourceID, rolepath string, operations []string) error
|
||||
GrantMultiple(resourceID string, roles []ResourcesRole) error
|
||||
|
||||
CheckAccess(resourceID, operation, sessionID string) error
|
||||
CheckAccessMulti(resourceID, operation, sessionID string) error
|
||||
CheckAccessMultiDetail(resourceID, operation, sessionID string) error
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
resourcesCreate = "/resources/%s"
|
||||
resourcesGet = "/resources/%s"
|
||||
resourcesDelete = "/resources/%s"
|
||||
resourcesGrant = "/resources/%s/grantPermission"
|
||||
resourcesCheckAccess = "/resources/%s/checkAccess?operation=%s&session=%s"
|
||||
resourcesCheckAccessMulti = "/resources/%s/checkMultiAccess?operation=%s&session=%s"
|
||||
resourcesCheckAccessMultiDetail = "/resources/%s/checkMultiAccess/detailed?operation=%s&session=%s"
|
||||
)
|
||||
|
||||
func (u *Resources) CheckAccessMulti(resourceID, operation, sessionID string) error {
|
||||
resp, err := u.Client.Get(fmt.Sprintf(resourcesCheckAccessMulti, resourceID, operation, sessionID))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Resources) CheckAccessMultiDetail(resourceID, operation, sessionID string) error {
|
||||
resp, err := u.Client.Get(fmt.Sprintf(resourcesCheckAccessMultiDetail, resourceID, operation, sessionID))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Resources) CheckAccess(resourceID, operation, sessionID string) error {
|
||||
resp, err := u.Client.Get(fmt.Sprintf(resourcesCheckAccess, resourceID, operation, sessionID))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Resources) Create(resourceID string, operations []string) error {
|
||||
body := struct {
|
||||
Operations []string `json:"operations"`
|
||||
}{operations}
|
||||
|
||||
resp, err := u.Client.Post(fmt.Sprintf(resourcesCreate, resourceID), body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Resources) Grant(resourceID, rolepath string, operations []string) error {
|
||||
body := make([]ResourcesRole, len(operations))
|
||||
for index, operation := range operations {
|
||||
body[index] = ResourcesRole{rolepath, operation}
|
||||
}
|
||||
return u.GrantMultiple(resourceID, body)
|
||||
}
|
||||
|
||||
func (u *Resources) GrantMultiple(resourceID string, roles []ResourcesRole) error {
|
||||
body := struct {
|
||||
Permissions []ResourcesRole `json:"permissions"`
|
||||
}{roles}
|
||||
|
||||
resp, err := u.Client.Patch(fmt.Sprintf(resourcesGrant, resourceID), body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Resources) Get(resourceID string) (*types.Resource, error) {
|
||||
resp, err := u.Client.Get(fmt.Sprintf(resourcesGet, resourceID))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
resource := &types.Resource{}
|
||||
return resource, errors.Wrap(json.NewDecoder(resp.Body).Decode(resource), "decoding json failed")
|
||||
default:
|
||||
return nil, toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Resources) Delete(resourceID string, resourceIDs ...string) error {
|
||||
deleteResource := func(resourceID string) error {
|
||||
resp, err := u.Client.Delete(fmt.Sprintf(resourcesDelete, resourceID))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
if err := deleteResource(resourceID); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, resourceID := range resourceIDs {
|
||||
if err := deleteResource(resourceID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ ResourcesInterface = &Resources{}
|
||||
@ -1,29 +0,0 @@
|
||||
package rbac_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResources(t *testing.T) {
|
||||
rbac, err := getClient()
|
||||
must(t, err, "Error when creating RBAC instance")
|
||||
|
||||
roles := rbac.Roles()
|
||||
resources := rbac.Resources()
|
||||
|
||||
roles.Delete("test-role")
|
||||
resources.Delete("test-resource")
|
||||
|
||||
must(t, roles.Create("test-role"), "Error when creating test-role")
|
||||
must(t, resources.Create("test-resource", []string{"view", "edit", "delete"}), "Error when creating test-resource")
|
||||
must(t, resources.Grant("test-resource", "test-role", []string{"view", "edit"}), "Error when granting permissions to role on resource")
|
||||
|
||||
{
|
||||
res, err := resources.Get("test-resource")
|
||||
must(t, err, "Error when retrieving test-resource")
|
||||
assert(t, res != nil, "Expected non-nil test-resource")
|
||||
}
|
||||
|
||||
must(t, resources.Delete("test-resource"), "Error deleting a resource")
|
||||
mustFail(t, resources.Delete("test-resource"))
|
||||
}
|
||||
@ -1,92 +0,0 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/crusttech/crust/internal/rbac/types"
|
||||
)
|
||||
|
||||
type (
|
||||
Roles struct {
|
||||
*Client
|
||||
}
|
||||
|
||||
RolesInterface interface {
|
||||
Create(rolepath string) error
|
||||
CreateNested(rolepaths ...string) error
|
||||
Get(rolepath string) (*types.Role, error)
|
||||
Delete(rolepath string) error
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
rolesCreate = "/roles/%s"
|
||||
rolesGet = "/roles/%s"
|
||||
rolesDelete = "/roles/%s"
|
||||
)
|
||||
|
||||
func (u *Roles) Create(rolepath string) error {
|
||||
if rolepath == "" {
|
||||
return errors.New("tried creating empty role")
|
||||
}
|
||||
resp, err := u.Client.Post(fmt.Sprintf(rolesCreate, rolepath), nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Roles) CreateNested(rolepaths ...string) error {
|
||||
if len(rolepaths) == 0 {
|
||||
return errors.New("tried creating empty role")
|
||||
}
|
||||
return u.Create(strings.Join(rolepaths, "/"))
|
||||
}
|
||||
|
||||
func (u *Roles) Get(rolepath string) (*types.Role, error) {
|
||||
resp, err := u.Client.Get(fmt.Sprintf(rolesDelete, rolepath))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
role := &types.Role{}
|
||||
return role, errors.Wrap(json.NewDecoder(resp.Body).Decode(role), "decoding json failed")
|
||||
default:
|
||||
return nil, toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Roles) GetNested(rolepaths ...string) (*types.Role, error) {
|
||||
if len(rolepaths) == 0 {
|
||||
return nil, errors.New("tried creating empty role")
|
||||
}
|
||||
return u.Get(strings.Join(rolepaths, "/"))
|
||||
}
|
||||
|
||||
func (u *Roles) Delete(rolepath string) error {
|
||||
resp, err := u.Client.Delete(fmt.Sprintf(rolesDelete, rolepath))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
var _ RolesInterface = &Roles{}
|
||||
@ -1,49 +0,0 @@
|
||||
package rbac_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestRoles(t *testing.T) {
|
||||
rbac, err := getClient()
|
||||
must(t, err, "Error when creating RBAC instance")
|
||||
|
||||
roles := rbac.Roles()
|
||||
roles.Delete("test-role")
|
||||
|
||||
mustFail(t, roles.CreateNested())
|
||||
must(t, roles.Create("test-role"), "Error when creating test-role")
|
||||
mustFail(t, roles.Create("test-role/nested/role"))
|
||||
must(t, roles.Create("test-role/nested"), "Error when creating deep nested role")
|
||||
must(t, roles.CreateNested("test-role", "nested", "role"), "Error when creating deep nested role")
|
||||
|
||||
{
|
||||
role, err := roles.Get("test-role")
|
||||
must(t, err, "Error when getting role")
|
||||
assert(t, role.Name == "test-role", "%+v", errors.Errorf("Unexpected role name, 'test-role' != '%s'", role.Name))
|
||||
}
|
||||
|
||||
{
|
||||
role, err := roles.Get("test-role/nested/role")
|
||||
must(t, err, "Error when getting role")
|
||||
assert(t, role.Name == "test-role/nested/role", "%+v", errors.Errorf("Unexpected role name, 'test-role/nested/role' != '%s'", role.Name))
|
||||
}
|
||||
|
||||
{
|
||||
role, err := roles.GetNested()
|
||||
mustFail(t, err)
|
||||
assert(t, role == nil, "%+v", errors.Errorf("Expected role=nil, got %#v", role))
|
||||
}
|
||||
|
||||
{
|
||||
role, err := roles.GetNested("test-role", "nested")
|
||||
must(t, err, "Error when getting role")
|
||||
assert(t, role.Name == "test-role/nested", "%+v", errors.Errorf("Unexpected role name, 'test-role/nested' != '%s'", role.Name))
|
||||
}
|
||||
|
||||
must(t, roles.Delete("test-role"), "Error when deleting test-role")
|
||||
mustFail(t, roles.Delete("non-existant"))
|
||||
mustFail(t, roles.Delete("test-role"))
|
||||
}
|
||||
@ -1,121 +0,0 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/crusttech/crust/internal/rbac/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type (
|
||||
Sessions struct {
|
||||
*Client
|
||||
}
|
||||
|
||||
SessionsInterface interface {
|
||||
Create(sessionID, userID string, roles ...string) error
|
||||
Get(sessionID string) (*types.Session, error)
|
||||
Delete(sessionID string) error
|
||||
|
||||
ActivateRole(sessionID string, roles ...string) error
|
||||
DeactivateRole(sessionID string, roles ...string) error
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
sessionsCreate = "/sessions/%s"
|
||||
sessionsGet = "/sessions/%s"
|
||||
sessionsDelete = "/sessions/%s"
|
||||
sessionsActivateRole = "/sessions/%s/activateRole"
|
||||
sessionsDeactivateRole = "/sessions/%s/deactivateRole"
|
||||
)
|
||||
|
||||
func (u *Sessions) Create(sessionID, userID string, roles ...string) error {
|
||||
body := struct {
|
||||
UserID string `json:"userid"`
|
||||
Roles []string `json:"roles,omitempty"`
|
||||
}{userID, roles}
|
||||
|
||||
resp, err := u.Client.Post(fmt.Sprintf(sessionsCreate, sessionID), body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Sessions) Get(sessionID string) (*types.Session, error) {
|
||||
resp, err := u.Client.Get(fmt.Sprintf(sessionsGet, sessionID))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
// @todo: fix session get response to return ID too
|
||||
session := &types.Session{
|
||||
ID: sessionID,
|
||||
}
|
||||
return session, errors.Wrap(json.NewDecoder(resp.Body).Decode(session), "decoding json failed")
|
||||
default:
|
||||
return nil, toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Sessions) Delete(sessionID string) error {
|
||||
resp, err := u.Client.Delete(fmt.Sprintf(sessionsDelete, sessionID))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Sessions) ActivateRole(sessionID string, roles ...string) error {
|
||||
body := struct {
|
||||
Roles []string `json:"roles"`
|
||||
}{roles}
|
||||
|
||||
resp, err := u.Client.Patch(fmt.Sprintf(sessionsActivateRole, sessionID), body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Sessions) DeactivateRole(sessionID string, roles ...string) error {
|
||||
body := struct {
|
||||
Roles []string `json:"roles"`
|
||||
}{roles}
|
||||
|
||||
resp, err := u.Client.Patch(fmt.Sprintf(sessionsDeactivateRole, sessionID), body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
var _ SessionsInterface = &Sessions{}
|
||||
@ -1,101 +0,0 @@
|
||||
package rbac_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestSessions(t *testing.T) {
|
||||
rbac, err := getClient()
|
||||
must(t, err, "Error when creating RBAC instance")
|
||||
|
||||
sessions := rbac.Sessions()
|
||||
users := rbac.Users()
|
||||
roles := rbac.Roles()
|
||||
resources := rbac.Resources()
|
||||
|
||||
// @todo until users.Get implements getting user by email, we need to delete users at end of the test successful and unsuccessful.
|
||||
sessions.Delete("test-session")
|
||||
roles.Delete("test-role")
|
||||
resources.Delete("test-resource")
|
||||
resources.Delete("team-1", "team-2", "team-3")
|
||||
|
||||
must(t, roles.Create("test-role"), "Error when creating test-role")
|
||||
|
||||
user, err := users.Create("test-user@crust.tech", "test-password")
|
||||
must(t, err, "Error when creating test-user@crust.tech")
|
||||
assert(t, user != nil, "%+v", errors.New("Expected non-nil user"))
|
||||
assert(t, user.ID != "", "%+v", errors.New("Expected non-empty user.ID"))
|
||||
assert(t, user.Username == "test-user@crust.tech", "%+v", errors.Errorf("Expected test-user@crust.tech == %s", user.Username))
|
||||
|
||||
must(t, users.AddRole(user.ID, "test-role"), "Error when assigning test-role to test-user@crust.tech")
|
||||
must(t, sessions.Create("test-session", user.ID, "test-role"), "Error when creating test-session")
|
||||
must(t, resources.Create("test-resource", []string{"view", "edit", "delete"}), "Error when creating test-resource")
|
||||
must(t, resources.Grant("test-resource", "test-role", []string{"view", "edit"}), "Error when granting permissions to role on resource")
|
||||
|
||||
// check role is created
|
||||
{
|
||||
session, err := sessions.Get("test-session")
|
||||
must(t, err, "Error when getting test-session")
|
||||
assert(t, session.ID == "test-session", "Unexpected Session ID, test-session != '%s'", session.ID)
|
||||
assert(t, len(session.Roles) == 1, "Expected one session role, got %+v", session.Roles)
|
||||
assert(t, session.Roles[0] == "test-role", "Unexpected session role, test-role != '%s'", session.Roles[0])
|
||||
}
|
||||
|
||||
// check user has permissions from role
|
||||
{
|
||||
must(t, resources.CheckAccess("test-resource", "view", "test-session"), "Owner has permission, but CheckAccess reports error")
|
||||
mustFail(t, resources.CheckAccess("test-resource", "delete", "test-session"))
|
||||
}
|
||||
|
||||
// check multi access
|
||||
{
|
||||
for i := 1; i <= 5; i++ {
|
||||
resources.Delete(fmt.Sprintf("team:%d", i))
|
||||
must(t, resources.Create(fmt.Sprintf("team:%d", i), []string{"edit"}), fmt.Sprintf("Error when creating team:%d", i))
|
||||
}
|
||||
mustFail(t, resources.CheckAccessMulti("team:*", "edit", "test-session"))
|
||||
resources.Grant("team:4", "test-role", []string{"edit"})
|
||||
must(t, resources.CheckAccess("team:4", "edit", "test-session"))
|
||||
must(t, resources.CheckAccessMulti("team:*", "edit", "test-session"))
|
||||
}
|
||||
|
||||
must(t, sessions.DeactivateRole("test-session", "test-role"), "Error when deactivating session role")
|
||||
|
||||
// check role is deactivated
|
||||
{
|
||||
session, err := sessions.Get("test-session")
|
||||
must(t, err, "Error when getting test-session")
|
||||
assert(t, session.ID == "test-session", "Unexpected Session ID, test-session != '%s'", session.ID)
|
||||
// assert(t, session.Username == "test-user", "Unexpected user, test-user != '%s'", session.Username)
|
||||
assert(t, len(session.Roles) == 0, "Expected one session role, got %+v", session.Roles)
|
||||
}
|
||||
|
||||
must(t, sessions.ActivateRole("test-session", "test-role"), "Error when deactivating session role")
|
||||
|
||||
// check role is activated
|
||||
{
|
||||
session, err := sessions.Get("test-session")
|
||||
must(t, err, "Error when getting test-session")
|
||||
assert(t, session.ID == "test-session", "Unexpected Session ID, test-session != '%s'", session.ID)
|
||||
// assert(t, session.Username == "test-user", "Unexpected user, test-user != '%s'", session.Username)
|
||||
assert(t, len(session.Roles) == 1, "Expected one session role, got %+v", session.Roles)
|
||||
assert(t, session.Roles[0] == "test-role", "Unexpected session role, test-role != '%s'", session.Roles[0])
|
||||
}
|
||||
|
||||
must(t, sessions.Delete("test-session"), "Error when deleting test-session")
|
||||
mustFail(t, func() error {
|
||||
_, err := sessions.Get("test-session")
|
||||
return err
|
||||
}())
|
||||
mustFail(t, sessions.Delete("test-session"))
|
||||
|
||||
must(t, users.Delete(user.ID), "Error when deleting test-user")
|
||||
mustFail(t, func() error {
|
||||
_, err := users.Get(user.ID)
|
||||
return err
|
||||
}())
|
||||
mustFail(t, users.Delete(user.ID))
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
package types
|
||||
|
||||
type (
|
||||
User struct {
|
||||
ID string `json:"userid"`
|
||||
Username string `json:"username"`
|
||||
AssignedRoles []string `json:"assignedRoles"`
|
||||
AuthorizedRoles []string `json:"authorizedRoles"`
|
||||
}
|
||||
|
||||
Session struct {
|
||||
ID string `json:"session"`
|
||||
UserID string `json:"userid"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
// @todo: need to list nested roles,
|
||||
// @todo: don't return users=null - return users: []?
|
||||
Role struct {
|
||||
Name string `json:"rolename"`
|
||||
Users []string `json:"users"`
|
||||
|
||||
// key = resource name
|
||||
Permissions map[string]Operations `json:"permissions"`
|
||||
}
|
||||
|
||||
Operations struct {
|
||||
Operations []string `json:"operations"`
|
||||
}
|
||||
|
||||
// @todo: read resource information
|
||||
Resource struct {
|
||||
}
|
||||
)
|
||||
@ -1,121 +0,0 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/crusttech/crust/internal/rbac/types"
|
||||
)
|
||||
|
||||
type (
|
||||
Users struct {
|
||||
*Client
|
||||
}
|
||||
|
||||
UsersInterface interface {
|
||||
Create(username, password string) (*types.User, error)
|
||||
Get(userID string) (*types.User, error)
|
||||
Delete(userID string) error
|
||||
|
||||
AddRole(userID string, roles ...string) error
|
||||
RemoveRole(userID string, roles ...string) error
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
usersCreate = "/users/"
|
||||
usersGet = "/users/%s"
|
||||
usersDelete = "/users/%s"
|
||||
// @todo: plural for users, but singular for sessions
|
||||
usersAddRole = "/users/%s/assignRoles"
|
||||
usersRemoveRole = "/users/%s/deassignRoles"
|
||||
)
|
||||
|
||||
func (u *Users) Create(username, password string) (*types.User, error) {
|
||||
body := struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}{username, password}
|
||||
|
||||
resp, err := u.Client.Post(usersCreate, body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
user := &types.User{}
|
||||
return user, errors.Wrap(json.NewDecoder(resp.Body).Decode(user), "decoding json failed")
|
||||
default:
|
||||
return nil, toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Users) AddRole(userID string, roles ...string) error {
|
||||
body := struct {
|
||||
Roles []string `json:"roles"`
|
||||
}{roles}
|
||||
|
||||
resp, err := u.Client.Patch(fmt.Sprintf(usersAddRole, userID), body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Users) RemoveRole(userID string, roles ...string) error {
|
||||
body := struct {
|
||||
Roles []string `json:"roles"`
|
||||
}{roles}
|
||||
|
||||
resp, err := u.Client.Patch(fmt.Sprintf(usersRemoveRole, userID), body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Users) Get(userID string) (*types.User, error) {
|
||||
resp, err := u.Client.Get(fmt.Sprintf(usersGet, userID))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
user := &types.User{}
|
||||
return user, errors.Wrap(json.NewDecoder(resp.Body).Decode(user), "decoding json failed")
|
||||
default:
|
||||
return nil, toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Users) Delete(userID string) error {
|
||||
resp, err := u.Client.Delete(fmt.Sprintf(usersDelete, userID))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch resp.StatusCode {
|
||||
case 200:
|
||||
return nil
|
||||
default:
|
||||
return toError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
var _ UsersInterface = &Users{}
|
||||
@ -1,55 +0,0 @@
|
||||
package rbac_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUsers(t *testing.T) {
|
||||
rbac, err := getClient()
|
||||
must(t, err, "Error when creating RBAC instance")
|
||||
|
||||
users := rbac.Users()
|
||||
roles := rbac.Roles()
|
||||
|
||||
// Cleanup data
|
||||
roles.Delete("test-role")
|
||||
// @todo until users.Get implements getting user by email, we need to delete users at end of the test successful and unsuccessful.
|
||||
|
||||
must(t, roles.Create("test-role"), "Error when creating test-role")
|
||||
|
||||
user, err := users.Create("test-user@crust.tech", "test-password")
|
||||
must(t, err, "Error when creating test-user")
|
||||
|
||||
// check if we inherited some roles (should be empty)
|
||||
{
|
||||
u1, err := users.Get(user.ID)
|
||||
must(t, err, "Error when retrieving test-user 1")
|
||||
assert(t, len(u1.AssignedRoles) == 0, "Unexpected number of roles, expected empty, got %+v", u1.AssignedRoles)
|
||||
}
|
||||
|
||||
must(t, users.AddRole(user.ID, "test-role"), "Error when assigning test-role to test-user")
|
||||
|
||||
// check if we inherited some roles (should be empty)
|
||||
{
|
||||
u2, err := users.Get(user.ID)
|
||||
must(t, err, "Error when retrieving test-user 2")
|
||||
assert(t, len(u2.AssignedRoles) == 1, "Unexpected number of roles, expected 1, got %+v", u2.AssignedRoles)
|
||||
assert(t, u2.AssignedRoles[0] == "test-role", "Unexpected role name, test-role != '%s'", u2.AssignedRoles[0])
|
||||
}
|
||||
|
||||
must(t, users.RemoveRole(user.ID, "test-role"), "Error when de-assigning test-role to test-user")
|
||||
|
||||
// check roles are empty after de-assign
|
||||
{
|
||||
u3, err := users.Get(user.ID)
|
||||
must(t, err, "Error when retrieving test-user 3")
|
||||
assert(t, len(u3.AssignedRoles) == 0, "Unexpected number of roles, expected empty, got %+v", u3.AssignedRoles)
|
||||
}
|
||||
|
||||
must(t, users.Delete(user.ID), "Error when deleting test-user")
|
||||
mustFail(t, func() error {
|
||||
_, err := users.Get(user.ID)
|
||||
return err
|
||||
}())
|
||||
mustFail(t, users.Delete(user.ID))
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func toError(resp *http.Response) error {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if body == nil || err != nil {
|
||||
return errors.Errorf("unexpected response (%d, %s)", resp.StatusCode, err)
|
||||
}
|
||||
return errors.New(string(body))
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user