Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: Unified Roles Management #9727

Merged
merged 10 commits into from
Aug 27, 2024
Merged
1 change: 1 addition & 0 deletions .drone.star
Original file line number Diff line number Diff line change
Expand Up @@ -2104,6 +2104,7 @@ def ocisServer(storage, accounts_hash_difficulty = 4, volumes = [], depends_on =
"NATS_NATS_PORT": 9233,
"OCIS_JWT_SECRET": "some-ocis-jwt-secret",
"EVENTHISTORY_STORE": "memory",
"GRAPH_AVAILABLE_ROLES": "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5,a8d5fe5e-96e3-418d-825b-534dbdf22b99,fb6c3e19-e378-47e5-b277-9732f9de6e21,58c63c02-1d89-4572-916a-870abc5a1b7d,2d00ce52-1fc2-4dbc-8b95-a73b73395f5a,1c996275-f1c9-4e71-abdf-a42f6495e960,312c0871-5ef7-4b3a-85b6-0e4074c64049,aa97fe03-7980-45ac-9e50-b325749fd7e6",
}

if deploy_type == "":
Expand Down
41 changes: 41 additions & 0 deletions changelog/unreleased/enhancement-unified-roles-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Enhancement: Unified Roles Management

Improved management of unified roles with the introduction of default enabled/disabled states and a new command for listing available roles.
It is important to note that a disabled role does not lose previously assigned permissions;
it only means that the role is not available for new assignments.

The following roles are now enabled by default:

- UnifiedRoleViewerID
- UnifiedRoleSpaceViewer
- UnifiedRoleEditor
- UnifiedRoleSpaceEditor
- UnifiedRoleFileEditor
- UnifiedRoleEditorLite
- UnifiedRoleManager

The following roles are now disabled by default:

- UnifiedRoleSecureViewer

To enable the UnifiedRoleSecureViewer role, you must provide a list of all available roles through one of the following methods:

- Using the GRAPH_AVAILABLE_ROLES environment variable.
- Setting the available_roles configuration value.

To enable a role, include the UID of the role in the list of available roles.

A new command has been introduced to simplify the process of finding out which UID belongs to which role. The command is:

```
$ ocis graph list-unified-roles
```

The output of this command includes the following information for each role:

- uid: The unique identifier of the role.
- Description: A short description of the role.
- Enabled: Whether the role is enabled or not.

https://github.com/owncloud/ocis/pull/9727
https://github.com/owncloud/ocis/issues/9698
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,8 @@ require (
github.com/longsleep/rndm v1.2.0 // indirect
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/maxymania/go-system v0.0.0-20170110133659-647cc364bf0b // indirect
github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103 // indirect
Expand Down Expand Up @@ -303,7 +303,7 @@ require (
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/statsd_exporter v0.22.8 // indirect
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/russellhaering/goxmldsig v1.4.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
Expand Down
11 changes: 6 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -819,13 +819,14 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
Expand Down Expand Up @@ -1043,8 +1044,8 @@ github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqn
github.com/riandyrn/otelchi v0.9.0 h1:BuQxXR7/JF2yYOQl21Yyz5d52hns/96ecAaPUZiKQzc=
github.com/riandyrn/otelchi v0.9.0/go.mod h1:iX30kllzThsf8oEcEbl3GifPJZtN4cnCWUUc+UhE4yM=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
Expand Down
2 changes: 1 addition & 1 deletion services/graph/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ l10n-push:

.PHONY: l10n-read
l10n-read: $(GO_XGETTEXT)
go-xgettext -o $(OUTPUT_DIR)/graph.pot --keyword=l10n.Template --add-comments -s pkg/service/v0/spacetemplates.go -s pkg/unifiedrole/unifiedrole.go
go-xgettext -o $(OUTPUT_DIR)/graph.pot --keyword=l10n.Template --add-comments -s pkg/service/v0/spacetemplates.go -s pkg/unifiedrole/roles.go

.PHONY: l10n-write
l10n-write:
Expand Down
7 changes: 4 additions & 3 deletions services/graph/pkg/command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import (

"github.com/owncloud/ocis/v2/ocis-pkg/clihelper"

"github.com/owncloud/ocis/v2/services/graph/pkg/config"
"github.com/urfave/cli/v2"

"github.com/owncloud/ocis/v2/services/graph/pkg/config"
)

// GetCommands provides all commands for this service
func GetCommands(cfg *config.Config) cli.Commands {
return []*cli.Command{
return append([]*cli.Command{
// start this service
Server(cfg),

Expand All @@ -20,7 +21,7 @@ func GetCommands(cfg *config.Config) cli.Commands {
// infos about this service
Health(cfg),
Version(cfg),
}
}, UnifiedRoles(cfg)...)
}

// Execute is the entry point for the ocis-graph command.
Expand Down
80 changes: 80 additions & 0 deletions services/graph/pkg/command/unified_roles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package command

import (
"os"
"slices"
"strings"

"github.com/olekukonko/tablewriter"
"github.com/urfave/cli/v2"

"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
"github.com/owncloud/ocis/v2/services/graph/pkg/config/parser"
"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
)

// UnifiedRoles bundles available commands for unified roles
func UnifiedRoles(cfg *config.Config) cli.Commands {
cmds := cli.Commands{
listUnifiedRoles(cfg),
}

for _, cmd := range cmds {
cmd.Category = "unified-roles"
cmd.Name = strings.Join([]string{cmd.Name, "unified-roles"}, "-")
cmd.Before = func(c *cli.Context) error {
return configlog.ReturnError(parser.ParseConfig(cfg))
}
}

return cmds
}

// unifiedRolesStatus lists available unified roles, it contains an indicator to show if the role is enabled or not
func listUnifiedRoles(cfg *config.Config) *cli.Command {
return &cli.Command{
Name: "list",
Usage: "list available unified roles",
Action: func(c *cli.Context) error {
tbl := tablewriter.NewWriter(os.Stdout)
tbl.SetRowLine(true)
tbl.SetAutoMergeCellsByColumnIndex([]int{0}) // rowspan should only affect the first column

headers := []string{"UID", "Enabled", "Description", "Condition", "Allowed resource actions"}
tbl.SetHeader(headers)

for _, definition := range unifiedrole.GetRoles(unifiedrole.RoleFilterAll()) {
const enabled = "enabled"
const disabled = "disabled"

rows := [][]string{
{definition.GetId(), disabled, definition.GetDescription()},
}
if slices.Contains(cfg.UnifiedRoles.AvailableRoles, definition.GetId()) {
rows[0][1] = enabled
}

for i, rolePermission := range definition.GetRolePermissions() {
actions := strings.Join(rolePermission.GetAllowedResourceActions(), "\n")
row := []string{rolePermission.GetCondition(), actions}
switch i {
case 0:
rows[0] = append(rows[0], row...)
default:
rows = append(rows, append(slices.Clone(rows[0][:len(rows[0])-len(row)]), row...))
}
}

for _, row := range rows {
// balance the row before adding it to the table,
// this prevents the row from having empty columns.
tbl.Append(append(row, make([]string, len(headers)-len(row))...))
}
}

tbl.Render()
return nil
},
}
}
11 changes: 6 additions & 5 deletions services/graph/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ type Config struct {
TokenManager *TokenManager `yaml:"token_manager"`
GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"`

Application Application `yaml:"application"`
Spaces Spaces `yaml:"spaces"`
Identity Identity `yaml:"identity"`
IncludeOCMSharees bool `yaml:"include_ocm_sharees" env:"OCIS_ENABLE_OCM;GRAPH_INCLUDE_OCM_SHAREES" desc:"Include OCM sharees when listing users." introductionVersion:"5.0"`
Events Events `yaml:"events"`
Application Application `yaml:"application"`
Spaces Spaces `yaml:"spaces"`
Identity Identity `yaml:"identity"`
IncludeOCMSharees bool `yaml:"include_ocm_sharees" env:"OCIS_ENABLE_OCM;GRAPH_INCLUDE_OCM_SHAREES" desc:"Include OCM sharees when listing users." introductionVersion:"5.0"`
Events Events `yaml:"events"`
UnifiedRoles UnifiedRoles `yaml:"unified_roles"`

Keycloak Keycloak `yaml:"keycloak"`
ServiceAccount ServiceAccount `yaml:"service_account"`
Expand Down
20 changes: 20 additions & 0 deletions services/graph/pkg/config/defaults/defaultconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import (
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
"github.com/owncloud/ocis/v2/ocis-pkg/structs"
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
)

var (
// _disabledByDefaultUnifiedRoleRoleIDs contains all roles that are not enabled by default,
// but can be enabled by the user.
_disabledByDefaultUnifiedRoleRoleIDs = []string{unifiedrole.UnifiedRoleSecureViewerID}
)

// FullDefaultConfig returns a fully initialized default configuration
Expand Down Expand Up @@ -107,6 +114,9 @@ func DefaultConfig() *config.Config {
Cluster: "ocis-cluster",
EnableTLS: false,
},
UnifiedRoles: config.UnifiedRoles{
AvailableRoles: nil, // will be populated with defaults in EnsureDefaults
},
}
}

Expand Down Expand Up @@ -164,6 +174,16 @@ func EnsureDefaults(cfg *config.Config) {
if cfg.Identity.LDAP.GroupCreateBaseDN == "" {
cfg.Identity.LDAP.GroupCreateBaseDN = cfg.Identity.LDAP.GroupBaseDN
}

// set default roles, if no roles are defined, we need to take care and provide all the default roles
if len(cfg.UnifiedRoles.AvailableRoles) == 0 {
for _, definition := range unifiedrole.GetRoles(
// filter out the roles that are disabled by default
unifiedrole.RoleFilterInvert(unifiedrole.RoleFilterIDs(_disabledByDefaultUnifiedRoleRoleIDs...)),
) {
cfg.UnifiedRoles.AvailableRoles = append(cfg.UnifiedRoles.AvailableRoles, definition.GetId())
}
}
}

// Sanitize sanitized the configuration
Expand Down
22 changes: 20 additions & 2 deletions services/graph/pkg/config/parser/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import (
"fmt"

"github.com/go-ldap/ldap/v3"

ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config"
defaults2 "github.com/owncloud/ocis/v2/ocis-pkg/config/defaults"
"github.com/owncloud/ocis/v2/ocis-pkg/config/envdecode"
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"

"github.com/owncloud/ocis/v2/ocis-pkg/config/envdecode"
"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
)

// ParseConfig loads configuration from known paths.
Expand Down Expand Up @@ -72,6 +73,23 @@ func Validate(cfg *config.Config) error {
return shared.MissingServiceAccountSecret(cfg.Service.Name)
}

// validate unified roles
{
var err error

for _, uid := range cfg.UnifiedRoles.AvailableRoles {
// check if the role is known
if len(unifiedrole.GetRoles(unifiedrole.RoleFilterIDs(uid))) == 0 {
// collect all possible errors to return them all at once
err = errors.Join(err, fmt.Errorf("%w: %s", unifiedrole.ErrUnknownRole, uid))
}
}

if err != nil {
return err
}
}

return nil
}

Expand Down
6 changes: 6 additions & 0 deletions services/graph/pkg/config/unified_roles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package config

// UnifiedRoles contains all settings related to unified roles.
type UnifiedRoles struct {
AvailableRoles []string `yaml:"available_roles" env:"GRAPH_AVAILABLE_ROLES" desc:"A list of roles that are available for assignment." introductionVersion:"%%NEXT%%"`
}
1 change: 1 addition & 0 deletions services/graph/pkg/linktype/linktype.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cs3org/reva/v2/pkg/storage/utils/grants"
libregraph "github.com/owncloud/libre-graph-api-go"

"github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole"
)

Expand Down
Loading