Skip to content

Commit da50ff6

Browse files
committed
RBAC checks for app list api
1 parent 728a317 commit da50ff6

File tree

6 files changed

+77
-42
lines changed

6 files changed

+77
-42
lines changed

internal/server/app_apis.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ func (s *Server) authenticateAndServeApp(w http.ResponseWriter, r *http.Request,
512512
}
513513

514514
s.Trace().Msgf("Authenticated user %s, doing authorization check", userId)
515-
authorized, err := s.rbacManager.AuthorizeAccess(userId, app.AppEntry.AppPathDomain(), string(appAuth), types.PermissionAccess)
515+
authorized, err := s.rbacManager.Authorize(userId, app.AppEntry.AppPathDomain(), string(appAuth), types.PermissionAccess)
516516
if err != nil {
517517
http.Error(w, err.Error(), http.StatusInternalServerError)
518518
return
@@ -916,8 +916,16 @@ func (s *Server) GetApps(ctx context.Context, appPathGlob string, internal bool)
916916
return nil, types.CreateRequestError(err.Error(), http.StatusBadRequest)
917917
}
918918

919+
userId := system.GetContextUserId(ctx)
919920
ret := make([]types.AppResponse, 0, len(filteredApps))
920921
for _, app := range filteredApps {
922+
authorized, err := s.AuthorizeList(userId, &app)
923+
if err != nil {
924+
return nil, types.CreateRequestError(err.Error(), http.StatusInternalServerError)
925+
}
926+
if !authorized {
927+
continue
928+
}
921929
retApp, err := s.GetApp(app.AppPathDomain, false)
922930
if err != nil {
923931
return nil, types.CreateRequestError(err.Error(), http.StatusInternalServerError)

internal/server/audit_middleware.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import (
1313
"sync/atomic"
1414
"time"
1515

16+
"github.com/go-chi/chi/middleware"
1617
"github.com/openrundev/openrun/internal/system"
1718
"github.com/openrundev/openrun/internal/types"
18-
"github.com/go-chi/chi/middleware"
1919
"github.com/segmentio/ksuid"
2020
)
2121

internal/server/openrun_plugin.go

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,30 +36,6 @@ type openrunPlugin struct {
3636
server *Server
3737
}
3838

39-
func (c *openrunPlugin) verifyHasAccess(userId string, appAuth types.AppAuthnType) bool {
40-
if appAuth == types.AppAuthnDefault {
41-
appAuth = types.AppAuthnType(c.server.config.Security.AppDefaultAuthType)
42-
}
43-
44-
// Verify user_id as set in authenticateAndServeApp
45-
if appAuth == "" || appAuth == types.AppAuthnNone {
46-
// No auth required for this app, allow access
47-
return true
48-
} else if appAuth == types.AppAuthnSystem {
49-
return userId == types.ADMIN_USER
50-
} else if appAuth == "cert" || strings.HasPrefix(string(appAuth), "cert_") {
51-
return userId == string(appAuth)
52-
} else {
53-
provider, _, ok := strings.Cut(string(userId), ":")
54-
if !ok {
55-
c.server.Warn().Str("user_id", userId).Msg("Unknown user_id format")
56-
return false
57-
}
58-
// Check Oauth provider is the same as the app's provider
59-
return provider == string(appAuth)
60-
}
61-
}
62-
6339
func (c *openrunPlugin) ListAllApps(thread *starlark.Thread, builtin *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
6440
return c.listAppsImpl(thread, builtin, args, kwargs, false, "list_all_apps")
6541
}
@@ -102,10 +78,6 @@ func (c *openrunPlugin) listAppsImpl(thread *starlark.Thread, _ *starlark.Builti
10278
userId := system.GetRequestUserId(thread)
10379
ret := starlark.List{}
10480
for _, app := range apps {
105-
if permCheck && !c.verifyHasAccess(userId, app.Auth) {
106-
continue
107-
}
108-
10981
// Filter out internal apps
11082
if app.MainApp != "" && !bool(include_internal) {
11183
continue
@@ -143,6 +115,16 @@ func (c *openrunPlugin) listAppsImpl(thread *starlark.Thread, _ *starlark.Builti
143115
}
144116
}
145117

118+
if permCheck {
119+
hasAccess, err := c.server.AuthorizeList(userId, &app)
120+
if err != nil {
121+
return nil, err
122+
}
123+
if !hasAccess {
124+
continue
125+
}
126+
}
127+
146128
v := starlark.Dict{}
147129
v.SetKey(starlark.String("name"), starlark.String(app.Name))
148130
v.SetKey(starlark.String("url"), starlark.String(types.GetAppUrl(app.AppPathDomain, c.server.config)))

internal/server/rbac.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,30 @@ func NewRBACHandler(logger *types.Logger, rbacConfig *types.RBACConfig, serverCo
4040
return rbacManager, nil
4141
}
4242

43-
func (h *RBACManager) AuthorizeAccess(user string, appPathDomain types.AppPathDomain, appAuthSetting string, permission types.RBACPermission) (bool, error) {
43+
func (h *RBACManager) Authorize(user string, appPathDomain types.AppPathDomain, appAuthSetting string, permission types.RBACPermission) (bool, error) {
4444
h.mu.RLock()
4545
defer h.mu.RUnlock()
4646

4747
if !h.rbacConfig.Enabled {
48-
// rbac is not enabled, authorize all access
48+
// rbac is not enabled, authorize all requests
4949
return true, nil
5050
}
5151

52-
if user != "" && user == h.serverConfig.AdminUser {
52+
if user != "" && user == types.ADMIN_USER {
5353
// admin user is always authorized if enabled
5454
return true, nil
5555
}
5656

57-
if !strings.HasPrefix(appAuthSetting, RBAC_AUTH_PREFIX) {
58-
// if app auth is not rbac enabled, authorize access
57+
if !strings.HasPrefix(appAuthSetting, RBAC_AUTH_PREFIX) && permission == types.PermissionAccess {
58+
// if app auth does not have rbac enabled, authorize access for Access permission
59+
// If authenticated, then app access is allowed
5960
return true, nil
6061
}
6162

63+
// Trim stage and preview suffixes, grant check are done on the main app path
64+
appPathDomain.Path = strings.TrimSuffix(appPathDomain.Path, types.STAGE_SUFFIX)
65+
appPathDomain.Path = strings.TrimSuffix(appPathDomain.Path, types.PREVIEW_SUFFIX)
66+
6267
return h.checkGrants(user, appPathDomain, permission)
6368
}
6469

internal/server/rbac_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ func TestAuthorizeAccess(t *testing.T) {
500500
t.Fatalf("failed to create RBACManager: %v", err)
501501
}
502502

503-
result, err := rbacManager.AuthorizeAccess(tt.user, tt.appPathDomain, tt.appAuthSetting, tt.permission)
503+
result, err := rbacManager.Authorize(tt.user, tt.appPathDomain, tt.appAuthSetting, tt.permission)
504504

505505
if tt.expectError {
506506
if err == nil {
@@ -600,7 +600,7 @@ func TestAuthorizeAccessWithGroupHierarchy(t *testing.T) {
600600
t.Fatalf("failed to create RBACManager: %v", err)
601601
}
602602

603-
result, err := rbacManager.AuthorizeAccess(tt.user, tt.appPathDomain, "rbac:test", tt.permission)
603+
result, err := rbacManager.Authorize(tt.user, tt.appPathDomain, "rbac:test", tt.permission)
604604
if err != nil {
605605
t.Errorf("unexpected error: %v", err)
606606
return
@@ -710,7 +710,7 @@ func TestAuthorizeAccessWithRoleHierarchy(t *testing.T) {
710710
t.Fatalf("failed to create RBACManager: %v", err)
711711
}
712712

713-
result, err := rbacManager.AuthorizeAccess(tt.user, tt.appPathDomain, "rbac:test", tt.permission)
713+
result, err := rbacManager.Authorize(tt.user, tt.appPathDomain, "rbac:test", tt.permission)
714714
if err != nil {
715715
t.Errorf("unexpected error: %v", err)
716716
return
@@ -833,7 +833,7 @@ func TestUpdateRBACConfig(t *testing.T) {
833833
}
834834

835835
// Test that the update worked by checking authorization
836-
result, err := rbacManager.AuthorizeAccess("user1", types.AppPathDomain{Path: "/new", Domain: ""}, "rbac:test", types.PermissionList)
836+
result, err := rbacManager.Authorize("user1", types.AppPathDomain{Path: "/new", Domain: ""}, "rbac:test", types.PermissionList)
837837
if err != nil {
838838
t.Errorf("unexpected error during authorization test: %v", err)
839839
return
@@ -884,7 +884,7 @@ func TestAuthorizeAccessConcurrency(t *testing.T) {
884884
go func(user string) {
885885
defer func() { done <- true }()
886886

887-
result, err := rbacManager.AuthorizeAccess(user, types.AppPathDomain{Path: "/test", Domain: ""}, "rbac:test", types.PermissionList)
887+
result, err := rbacManager.Authorize(user, types.AppPathDomain{Path: "/test", Domain: ""}, "rbac:test", types.PermissionList)
888888
if err != nil {
889889
t.Errorf("unexpected error: %v", err)
890890
return

internal/server/server.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,12 +300,12 @@ func (s *Server) UpdateDynamicConfig(ctx context.Context, newConfig *types.Dynam
300300
}
301301

302302
newConfig.VersionId = "ver_" + ksuid.New().String()
303-
err := s.db.UpdateConfig(ctx, system.GetContextUserId(ctx), currentVersionId, newConfig)
303+
err := s.updateDynamicConfigCache(ctx, newConfig)
304304
if err != nil {
305305
return nil, fmt.Errorf("error updating dynamic config: %w", err)
306306
}
307307

308-
err = s.updateDynamicConfigCache(ctx, newConfig)
308+
err = s.db.UpdateConfig(ctx, system.GetContextUserId(ctx), currentVersionId, newConfig)
309309
if err != nil {
310310
return nil, fmt.Errorf("error updating dynamic config: %w", err)
311311
}
@@ -841,3 +841,43 @@ func (s *Server) ParseGlob(appGlob string) ([]types.AppInfo, error) {
841841

842842
return matched, nil
843843
}
844+
845+
// AuthorizeList checks if the user has access to perform list operation on the specified app
846+
// For RBAC mode, uses RBAC permissions. For non-RBAC mode, look at whether app is using
847+
// same authentication types as used by the caller
848+
func (s *Server) AuthorizeList(userId string, app *types.AppInfo) (bool, error) {
849+
appAuthStr := string(app.Auth)
850+
if s.rbacManager.rbacConfig.Enabled {
851+
// RBAC auth is enabled, verify access
852+
return s.rbacManager.Authorize(userId, app.AppPathDomain, appAuthStr, types.PermissionList)
853+
}
854+
855+
if userId != "" && userId == types.ADMIN_USER {
856+
// Admin user is always authorized
857+
return true, nil
858+
}
859+
860+
appAuthStr = strings.TrimPrefix(appAuthStr, RBAC_AUTH_PREFIX)
861+
appAuth := types.AppAuthnType(appAuthStr)
862+
if appAuth == types.AppAuthnDefault {
863+
appAuth = types.AppAuthnType(s.config.Security.AppDefaultAuthType)
864+
}
865+
866+
// Verify user_id as set in authenticateAndServeApp
867+
if appAuth == "" || appAuth == types.AppAuthnNone {
868+
// No auth required for this app, authorize access
869+
return true, nil
870+
} else if appAuth == types.AppAuthnSystem {
871+
return userId != "" && userId == types.ADMIN_USER, nil
872+
} else if appAuth == "cert" || strings.HasPrefix(string(appAuth), "cert_") {
873+
return userId == string(appAuth), nil
874+
} else {
875+
provider, _, ok := strings.Cut(string(userId), ":")
876+
if !ok {
877+
s.Warn().Str("user_id", userId).Msg("Unknown user_id format")
878+
return false, nil
879+
}
880+
// Check Oauth provider is the same as the app's provider
881+
return provider == string(appAuth), nil
882+
}
883+
}

0 commit comments

Comments
 (0)