Skip to content

Commit

Permalink
Delete user rbac attachments on deletion of users (#271)
Browse files Browse the repository at this point in the history
* Delete user rbac attachments on deletion of users
* Add a DeleteUser method so we can delete rbac attachments for a user.
* Make noop.go fully no-op (makes sure that a call to DeleteUser) even with RBAC off is OK. All other endpoints now fully no-op for consistency.
* Introduce a UserHandler to contain dependencies for the user handlers

* Fix tests post merge

* Move handler deps to providers
* Created a RBAC Provider
* Created a User Provider

* Fix linting issues
  • Loading branch information
speza authored Jul 15, 2020
1 parent c108600 commit f588ae4
Show file tree
Hide file tree
Showing 13 changed files with 273 additions and 192 deletions.
11 changes: 11 additions & 0 deletions gaia.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"os"
"time"

"github.com/dgrijalva/jwt-go"
hclog "github.com/hashicorp/go-hclog"
"github.com/robfig/cron"
)
Expand Down Expand Up @@ -156,6 +157,16 @@ const (
StartReasonScheduled = "scheduled"
)

// JwtExpiry is the default JWT expiry.
const JwtExpiry = 12 * 60 * 60

// JwtCustomClaims is the custom JWT claims for a Gaia session.
type JwtCustomClaims struct {
Username string `json:"username"`
Roles []string `json:"roles"`
jwt.StandardClaims
}

// User is the user object
type User struct {
Username string `json:"username,omitempty"`
Expand Down
32 changes: 16 additions & 16 deletions handlers/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,11 @@ func TestAuthBarrierHMACTokenWithHMACKey(t *testing.T) {
JWTKey: []byte("hmac-jwt-key"),
}

claims := jwtCustomClaims{
claims := gaia.JwtCustomClaims{
Username: "test-user",
Roles: []string{},
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Unix() + jwtExpiry,
ExpiresAt: time.Now().Unix() + gaia.JwtExpiry,
IssuedAt: time.Now().Unix(),
Subject: "Gaia Session Token",
},
Expand Down Expand Up @@ -164,11 +164,11 @@ func TestAuthBarrierRSATokenWithRSAKey(t *testing.T) {
JWTKey: key,
}

claims := jwtCustomClaims{
claims := gaia.JwtCustomClaims{
Username: "test-user",
Roles: []string{},
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Unix() + jwtExpiry,
ExpiresAt: time.Now().Unix() + gaia.JwtExpiry,
IssuedAt: time.Now().Unix(),
Subject: "Gaia Session Token",
},
Expand Down Expand Up @@ -199,11 +199,11 @@ func TestAuthBarrierHMACTokenWithRSAKey(t *testing.T) {
JWTKey: &rsa.PrivateKey{},
}

claims := jwtCustomClaims{
claims := gaia.JwtCustomClaims{
Username: "test-user",
Roles: []string{},
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Unix() + jwtExpiry,
ExpiresAt: time.Now().Unix() + gaia.JwtExpiry,
IssuedAt: time.Now().Unix(),
Subject: "Gaia Session Token",
},
Expand Down Expand Up @@ -242,11 +242,11 @@ func TestAuthBarrierRSATokenWithHMACKey(t *testing.T) {
JWTKey: []byte("hmac-jwt-key"),
}

claims := jwtCustomClaims{
claims := gaia.JwtCustomClaims{
Username: "test-user",
Roles: []string{},
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Unix() + jwtExpiry,
ExpiresAt: time.Now().Unix() + gaia.JwtExpiry,
IssuedAt: time.Now().Unix(),
Subject: "Gaia Session Token",
},
Expand Down Expand Up @@ -298,11 +298,11 @@ func TestAuthBarrierNoPerms(t *testing.T) {
JWTKey: []byte("hmac-jwt-key"),
}

claims := jwtCustomClaims{
claims := gaia.JwtCustomClaims{
Username: "test-user",
Roles: []string{},
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Unix() + jwtExpiry,
ExpiresAt: time.Now().Unix() + gaia.JwtExpiry,
IssuedAt: time.Now().Unix(),
Subject: "Gaia Session Token",
},
Expand Down Expand Up @@ -333,11 +333,11 @@ func TestAuthBarrierAllPerms(t *testing.T) {
JWTKey: []byte("hmac-jwt-key"),
}

claims := jwtCustomClaims{
claims := gaia.JwtCustomClaims{
Username: "test-user",
Roles: []string{"CatOneGetSingle", "CatOnePostSingle", "CatTwoGetSingle", "CatTwoPostSingle"},
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Unix() + jwtExpiry,
ExpiresAt: time.Now().Unix() + gaia.JwtExpiry,
IssuedAt: time.Now().Unix(),
Subject: "Gaia Session Token",
},
Expand Down Expand Up @@ -386,11 +386,11 @@ func Test_AuthMiddleware_Enforcer_PermissionDenied(t *testing.T) {
JWTKey: []byte("hmac-jwt-key"),
}

claims := jwtCustomClaims{
claims := gaia.JwtCustomClaims{
Username: "enforcer-perms-err",
Roles: []string{},
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Unix() + jwtExpiry,
ExpiresAt: time.Now().Unix() + gaia.JwtExpiry,
IssuedAt: time.Now().Unix(),
Subject: "Gaia Session Token",
},
Expand Down Expand Up @@ -420,11 +420,11 @@ func Test_AuthMiddleware_Enforcer_UnknownError(t *testing.T) {
}
gaia.Cfg.Logger = hclog.NewNullLogger()

claims := jwtCustomClaims{
claims := gaia.JwtCustomClaims{
Username: "enforcer-err",
Roles: []string{},
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Unix() + jwtExpiry,
ExpiresAt: time.Now().Unix() + gaia.JwtExpiry,
IssuedAt: time.Now().Unix(),
Subject: "Gaia Session Token",
},
Expand Down
37 changes: 15 additions & 22 deletions handlers/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,14 @@ func (s *GaiaHandler) InitHandlers(e *echo.Echo) error {

// Endpoints for Gaia primary instance
if gaia.Cfg.Mode == gaia.ModeServer {
// Users
apiGrp.POST("login", UserLogin)

apiAuthGrp.GET("users", UserGetAll)
apiAuthGrp.POST("user/password", UserChangePassword)
apiAuthGrp.DELETE("user/:username", UserDelete)
apiAuthGrp.GET("user/:username/permissions", UserGetPermissions)
apiAuthGrp.PUT("user/:username/permissions", UserPutPermissions)
apiAuthGrp.POST("user", UserAdd)
apiAuthGrp.PUT("user/:username/reset-trigger-token", UserResetTriggerToken)

apiGrp.POST("login", s.deps.UserProvider.UserLogin)
apiAuthGrp.GET("users", s.deps.UserProvider.UserGetAll)
apiAuthGrp.POST("user/password", s.deps.UserProvider.UserChangePassword)
apiAuthGrp.DELETE("user/:username", s.deps.UserProvider.UserDelete)
apiAuthGrp.GET("user/:username/permissions", s.deps.UserProvider.UserGetPermissions)
apiAuthGrp.PUT("user/:username/permissions", s.deps.UserProvider.UserPutPermissions)
apiAuthGrp.POST("user", s.deps.UserProvider.UserAdd)
apiAuthGrp.PUT("user/:username/reset-trigger-token", s.deps.UserProvider.UserResetTriggerToken)
apiAuthGrp.GET("permission", PermissionGetAll)

// Pipelines
Expand Down Expand Up @@ -86,19 +83,15 @@ func (s *GaiaHandler) InitHandlers(e *echo.Echo) error {
apiAuthGrp.POST("secret", SetSecret)
apiAuthGrp.PUT("secret/update", SetSecret)

// RBAC
rbacHandler := rbacHandler{
svc: s.deps.RBACService,
}
// RBAC - Management
apiAuthGrp.GET("rbac/roles", rbacHandler.getAllRoles)
apiAuthGrp.PUT("rbac/roles/:role", rbacHandler.addRole)
apiAuthGrp.DELETE("rbac/roles/:role", rbacHandler.deleteRole)
apiAuthGrp.PUT("rbac/roles/:role/attach/:username", rbacHandler.attachRole)
apiAuthGrp.DELETE("rbac/roles/:role/attach/:username", rbacHandler.detachRole)
apiAuthGrp.GET("rbac/roles/:role/attached", rbacHandler.getRolesAttachedUsers)
apiAuthGrp.GET("rbac/roles", s.deps.RBACProvider.GetAllRoles)
apiAuthGrp.PUT("rbac/roles/:role", s.deps.RBACProvider.AddRole)
apiAuthGrp.DELETE("rbac/roles/:role", s.deps.RBACProvider.DeleteRole)
apiAuthGrp.PUT("rbac/roles/:role/attach/:username", s.deps.RBACProvider.DetachRole)
apiAuthGrp.DELETE("rbac/roles/:role/attach/:username", s.deps.RBACProvider.DetachRole)
apiAuthGrp.GET("rbac/roles/:role/attached", s.deps.RBACProvider.GetRolesAttachedUsers)
// RBAC - Users
apiAuthGrp.GET("users/:username/rbac/roles", rbacHandler.getUserAttachedRoles)
apiAuthGrp.GET("users/:username/rbac/roles", s.deps.RBACProvider.GetUserAttachedRoles)
}

// Worker
Expand Down
3 changes: 3 additions & 0 deletions handlers/service.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package handlers

import (
"github.com/gaia-pipeline/gaia/providers"
"github.com/gaia-pipeline/gaia/providers/pipelines"
"github.com/gaia-pipeline/gaia/providers/workers"
"github.com/gaia-pipeline/gaia/security"
Expand All @@ -15,6 +16,8 @@ type Dependencies struct {
Scheduler service.GaiaScheduler
PipelineService pipeline.Servicer
PipelineProvider pipelines.PipelineProviderer
UserProvider providers.UserProvider
RBACProvider providers.RBACProvider
WorkerProvider workers.WorkerProviderer
Certificate security.CAAPI
RBACService rbac.Service
Expand Down
28 changes: 28 additions & 0 deletions providers/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package providers

import (
"github.com/labstack/echo"
)

// RBACProvider provides all the handler endpoints for RBAC actions.
type RBACProvider interface {
AddRole(c echo.Context) error
DeleteRole(c echo.Context) error
GetAllRoles(c echo.Context) error
GetUserAttachedRoles(c echo.Context) error
GetRolesAttachedUsers(c echo.Context) error
AttachRole(c echo.Context) error
DetachRole(c echo.Context) error
}

// UserProvider provides all the handler endpoints for User actions.
type UserProvider interface {
UserLogin(c echo.Context) error
UserGetAll(c echo.Context) error
UserChangePassword(c echo.Context) error
UserResetTriggerToken(c echo.Context) error
UserDelete(c echo.Context) error
UserAdd(c echo.Context) error
UserGetPermissions(c echo.Context) error
UserPutPermissions(c echo.Context) error
}
31 changes: 22 additions & 9 deletions handlers/rbac.go → providers/rbac/provider.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package handlers
package rbac

import (
"net/http"
Expand All @@ -9,11 +9,18 @@ import (
"github.com/gaia-pipeline/gaia/security/rbac"
)

type rbacHandler struct {
// Provider represents the RBAC provider.
type Provider struct {
svc rbac.Service
}

func (h *rbacHandler) addRole(c echo.Context) error {
// NewProvider creates a new Provider.
func NewProvider(svc rbac.Service) *Provider {
return &Provider{svc: svc}
}

// AddRole adds an RBAC role using the RBAC service.
func (h *Provider) AddRole(c echo.Context) error {
role := c.Param("role")
if role == "" {
return c.String(http.StatusBadRequest, "Must provide role.")
Expand All @@ -33,7 +40,8 @@ func (h *rbacHandler) addRole(c echo.Context) error {
return c.String(http.StatusOK, "Role created successfully.")
}

func (h *rbacHandler) deleteRole(c echo.Context) error {
// DeleteRole deletes an RBAC role using the RBAC service.
func (h *Provider) DeleteRole(c echo.Context) error {
role := c.Param("role")
if role == "" {
return c.String(http.StatusBadRequest, "Must provide role.")
Expand All @@ -47,11 +55,13 @@ func (h *rbacHandler) deleteRole(c echo.Context) error {
return c.String(http.StatusOK, "Role deleted successfully.")
}

func (h *rbacHandler) getAllRoles(c echo.Context) error {
// GetAllRoles gets all RBAC roles.
func (h *Provider) GetAllRoles(c echo.Context) error {
return c.JSON(http.StatusOK, h.svc.GetAllRoles())
}

func (h *rbacHandler) getUserAttachedRoles(c echo.Context) error {
// GetUserAttachedRoles gets all roles attached to a specific user.
func (h *Provider) GetUserAttachedRoles(c echo.Context) error {
username := c.Param("username")
if username == "" {
return c.String(http.StatusBadRequest, "Must provide username.")
Expand All @@ -66,7 +76,8 @@ func (h *rbacHandler) getUserAttachedRoles(c echo.Context) error {
return c.JSON(http.StatusOK, roles)
}

func (h *rbacHandler) getRolesAttachedUsers(c echo.Context) error {
// GetRolesAttachedUsers gets all users attached to a role.
func (h *Provider) GetRolesAttachedUsers(c echo.Context) error {
role := c.Param("role")
if role == "" {
return c.String(http.StatusBadRequest, "Must provide role.")
Expand All @@ -81,7 +92,8 @@ func (h *rbacHandler) getRolesAttachedUsers(c echo.Context) error {
return c.JSON(http.StatusOK, roles)
}

func (h *rbacHandler) attachRole(c echo.Context) error {
// AttachRole attches a role to a user.
func (h *Provider) AttachRole(c echo.Context) error {
role := c.Param("role")
if role == "" {
return c.String(http.StatusBadRequest, "Must provide role.")
Expand All @@ -100,7 +112,8 @@ func (h *rbacHandler) attachRole(c echo.Context) error {
return c.String(http.StatusOK, "Role attached successfully.")
}

func (h *rbacHandler) detachRole(c echo.Context) error {
// DetachRole deteches a role from a user.
func (h *Provider) DetachRole(c echo.Context) error {
role := c.Param("role")
if role == "" {
return c.String(http.StatusBadRequest, "Must provide role.")
Expand Down
Loading

0 comments on commit f588ae4

Please sign in to comment.