diff --git a/cmd/crm/main.go b/cmd/crm/main.go index 3fe99e8b7..f9d2e707b 100644 --- a/cmd/crm/main.go +++ b/cmd/crm/main.go @@ -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 diff --git a/cmd/crust/main.go b/cmd/crust/main.go index fb9be9822..db069dc22 100644 --- a/cmd/crust/main.go +++ b/cmd/crust/main.go @@ -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() diff --git a/cmd/messaging/main.go b/cmd/messaging/main.go index c7225921a..640674421 100644 --- a/cmd/messaging/main.go +++ b/cmd/messaging/main.go @@ -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 diff --git a/cmd/system-cli/main.go b/cmd/system-cli/main.go index 0ecdbc61b..fa467e2d5 100644 --- a/cmd/system-cli/main.go +++ b/cmd/system-cli/main.go @@ -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() diff --git a/cmd/system/main.go b/cmd/system/main.go index c9460ffc5..1c291d6a7 100644 --- a/cmd/system/main.go +++ b/cmd/system/main.go @@ -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) diff --git a/internal/config/rbac.go b/internal/config/rbac.go deleted file mode 100644 index 32ada3760..000000000 --- a/internal/config/rbac.go +++ /dev/null @@ -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 -} diff --git a/internal/rbac/client.go b/internal/rbac/client.go deleted file mode 100644 index a51e142eb..000000000 --- a/internal/rbac/client.go +++ /dev/null @@ -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 -} diff --git a/internal/rbac/flags.go b/internal/rbac/flags.go deleted file mode 100644 index a70743cb3..000000000 --- a/internal/rbac/flags.go +++ /dev/null @@ -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) -} diff --git a/internal/rbac/interfaces.txt b/internal/rbac/interfaces.txt deleted file mode 100644 index 720e46e73..000000000 --- a/internal/rbac/interfaces.txt +++ /dev/null @@ -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 - } -) diff --git a/internal/rbac/main_test.go b/internal/rbac/main_test.go deleted file mode 100644 index 301c4d2ab..000000000 --- a/internal/rbac/main_test.go +++ /dev/null @@ -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") -} diff --git a/internal/rbac/resources.go b/internal/rbac/resources.go deleted file mode 100644 index 05c7a03d3..000000000 --- a/internal/rbac/resources.go +++ /dev/null @@ -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{} diff --git a/internal/rbac/resources_test.go b/internal/rbac/resources_test.go deleted file mode 100644 index 0ef92990e..000000000 --- a/internal/rbac/resources_test.go +++ /dev/null @@ -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")) -} diff --git a/internal/rbac/roles.go b/internal/rbac/roles.go deleted file mode 100644 index 280896120..000000000 --- a/internal/rbac/roles.go +++ /dev/null @@ -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{} diff --git a/internal/rbac/roles_test.go b/internal/rbac/roles_test.go deleted file mode 100644 index 3f8c56b4a..000000000 --- a/internal/rbac/roles_test.go +++ /dev/null @@ -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")) -} diff --git a/internal/rbac/sessions.go b/internal/rbac/sessions.go deleted file mode 100644 index ec3e06503..000000000 --- a/internal/rbac/sessions.go +++ /dev/null @@ -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{} diff --git a/internal/rbac/sessions_test.go b/internal/rbac/sessions_test.go deleted file mode 100644 index 3d6b0a419..000000000 --- a/internal/rbac/sessions_test.go +++ /dev/null @@ -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)) -} diff --git a/internal/rbac/types/structs.go b/internal/rbac/types/structs.go deleted file mode 100644 index 099734613..000000000 --- a/internal/rbac/types/structs.go +++ /dev/null @@ -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 { - } -) diff --git a/internal/rbac/users.go b/internal/rbac/users.go deleted file mode 100644 index fb209d93e..000000000 --- a/internal/rbac/users.go +++ /dev/null @@ -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{} diff --git a/internal/rbac/users_test.go b/internal/rbac/users_test.go deleted file mode 100644 index d75cb978b..000000000 --- a/internal/rbac/users_test.go +++ /dev/null @@ -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)) -} diff --git a/internal/rbac/util.go b/internal/rbac/util.go deleted file mode 100644 index e9260c689..000000000 --- a/internal/rbac/util.go +++ /dev/null @@ -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)) -}