jamesread 5 лет назад
Родитель
Сommit
5a151859d7
5 измененных файлов с 144 добавлено и 15 удалено
  1. 3 0
      OliveTin.proto
  2. 84 0
      internal/acl/acl.go
  3. 21 0
      internal/config/config.go
  4. 4 13
      internal/executor/executor.go
  5. 32 2
      internal/grpcapi/grpcApi.go

+ 3 - 0
OliveTin.proto

@@ -8,6 +8,7 @@ message ActionButton {
 	string id = 1;
 	string title = 2;
 	string icon = 3;
+	bool canExec = 4;
 }
 
 message GetButtonsResponse {
@@ -34,6 +35,8 @@ message LogEntry {
 	string stderr = 4;
 	bool timedOut = 5;
 	int32 exitCode = 6;
+	string user = 7;
+	string userClass = 8;
 }
 
 message GetLogsResponse {

+ 84 - 0
internal/acl/acl.go

@@ -0,0 +1,84 @@
+package acl
+
+import (
+	config "github.com/jamesread/OliveTin/internal/config"
+	log "github.com/sirupsen/logrus"
+	"context"
+)
+
+type User struct {
+	Username string;
+}
+
+func IsAllowedExec(cfg *config.Config, user *User, action *config.ActionButton) bool {
+	canExec := cfg.DefaultPermissions.Exec
+
+	log.WithFields(log.Fields{
+				"User": user.Username,
+				"Action": action.Title,
+				"CanExec": canExec,
+	}).Debug("isAllowedExec Permission Default")
+
+	for _, permissionEntry := range action.Permissions {
+		if isUserInGroup(user, permissionEntry.Usergroup) {
+			log.WithFields(log.Fields{
+				"User": user.Username,
+				"Action": action.Title,
+				"CanExec": permissionEntry.Exec,
+			}).Debug("isAllowedExec Permission Entry")
+
+			canExec = permissionEntry.Exec
+		}
+	}
+
+	log.WithFields(log.Fields{
+		"User": user.Username,
+		"Action": action.Title,
+		"CanExec": canExec,
+	}).Debug("isAllowedExec Final Result")
+
+	return canExec;
+}
+
+func IsAllowedView(cfg *config.Config, user *User, action *config.ActionButton) bool {
+	canView := cfg.DefaultPermissions.View
+
+	log.WithFields(log.Fields{
+				"User": user.Username,
+				"Action": action.Title,
+				"CanView": canView,
+	}).Debug("isAllowedView Permission Default")
+
+	for idx, permissionEntry := range action.Permissions {
+		if isUserInGroup(user, permissionEntry.Usergroup) {
+			log.WithFields(log.Fields{
+				"User": user.Username,
+				"Action": action.Title,
+				"CanView": permissionEntry.View,
+				"Index": idx,
+			}).Debug("isAllowedView Permission Entry")
+
+			canView = permissionEntry.View
+		}
+	}
+
+	log.WithFields(log.Fields{
+		"User": user.Username,
+		"Action": action.Title,
+		"CanView": canView,
+	}).Debug("isAllowedView Final Result")
+
+	return canView;
+}
+
+
+
+func isUserInGroup(user *User, usergroup string) bool {
+	return true;
+}
+
+func UserFromContext(ctx context.Context) *User {
+	return &User {
+		Username: "Guest",
+	}
+}

+ 21 - 0
internal/config/config.go

@@ -10,6 +10,7 @@ type ActionButton struct {
 	Shell   string
 	CSS     map[string]string `mapstructure:"omitempty"`
 	Timeout int
+	Permissions []PermissionsEntry
 }
 
 // Entity represents a "thing" that can have multiple actions associated with it.
@@ -21,6 +22,22 @@ type Entity struct {
 	CSS           map[string]string
 }
 
+type PermissionsEntry struct {
+	Usergroup string
+	View bool
+	Exec bool
+}
+
+type DefaultPermissions struct {
+	View bool
+	Exec bool
+}
+
+type UserGroup struct {
+	Name string
+	Members []string
+}
+
 // Config is the global config used through the whole app.
 type Config struct {
 	UseSingleHTTPFrontend           bool
@@ -34,6 +51,8 @@ type Config struct {
 	ActionButtons                   []ActionButton `mapstructure:"actions"`
 	Entities                        []Entity       `mapstructure:"omitempty"`
 	CheckForUpdates                 bool
+	Usergroups						[]UserGroup
+	DefaultPermissions				DefaultPermissions
 }
 
 // DefaultConfig gets a new Config structure with sensible default values.
@@ -46,6 +65,8 @@ func DefaultConfig() *Config {
 	config.ListenAddressWebUI = "localhost:1340"
 	config.LogLevel = "INFO"
 	config.CheckForUpdates = true
+	config.DefaultPermissions.Exec = true
+	config.DefaultPermissions.View = true
 
 	return &config
 }

+ 4 - 13
internal/executor/executor.go

@@ -4,6 +4,7 @@ import (
 	pb "github.com/jamesread/OliveTin/gen/grpc"
 	config "github.com/jamesread/OliveTin/internal/config"
 	log "github.com/sirupsen/logrus"
+	acl "github.com/jamesread/OliveTin/internal/acl"
 
 	"context"
 	"errors"
@@ -26,21 +27,11 @@ type Executor struct {
 }
 
 // ExecAction executes an action.
-func (e *Executor) ExecAction(cfg *config.Config, action string) *pb.StartActionResponse {
+func (e *Executor) ExecAction(cfg *config.Config, user *acl.User, actualAction *config.ActionButton) *pb.StartActionResponse {
 	log.WithFields(log.Fields{
-		"actionName": action,
+		"actionName": actualAction.Title,
 	}).Infof("StartAction")
 
-	actualAction, err := findAction(cfg, action)
-
-	if err != nil {
-		log.Errorf("Error finding action %s, %s", err, action)
-
-		return &pb.StartActionResponse{
-			LogEntry: nil,
-		}
-	}
-
 	res := execAction(cfg, actualAction)
 
 	e.Logs = append(e.Logs, *res);
@@ -103,7 +94,7 @@ func sanitizeAction(action *config.ActionButton) {
 	}
 }
 
-func findAction(cfg *config.Config, actionTitle string) (*config.ActionButton, error) {
+func FindAction(cfg *config.Config, actionTitle string) (*config.ActionButton, error) {
 	for _, action := range cfg.ActionButtons {
 		if action.Title == actionTitle {
 			sanitizeAction(&action)

+ 32 - 2
internal/grpcapi/grpcApi.go

@@ -11,6 +11,7 @@ import (
 
 	config "github.com/jamesread/OliveTin/internal/config"
 	executor "github.com/jamesread/OliveTin/internal/executor"
+	acl "github.com/jamesread/OliveTin/internal/acl"
 )
 
 var (
@@ -23,23 +24,52 @@ type oliveTinAPI struct {
 }
 
 func (api *oliveTinAPI) StartAction(ctx ctx.Context, req *pb.StartActionRequest) (*pb.StartActionResponse, error) {
-	return ex.ExecAction(cfg, req.ActionName), nil
+	actualAction, err := executor.FindAction(cfg, req.ActionName)
+
+	if err != nil {
+		log.Errorf("Error finding action %s, %s", err, req.ActionName)
+
+		return &pb.StartActionResponse{
+			LogEntry: nil,
+		}, nil
+	}
+
+	user := acl.UserFromContext(ctx)
+
+	if !acl.IsAllowedExec(cfg, user, actualAction) {
+		return &pb.StartActionResponse{
+		}, nil
+
+	}
+
+	return ex.ExecAction(cfg, acl.UserFromContext(ctx), actualAction), nil
 }
 
 func (api *oliveTinAPI) GetButtons(ctx ctx.Context, req *pb.GetButtonsRequest) (*pb.GetButtonsResponse, error) {
 	res := &pb.GetButtonsResponse{}
 
+	user := acl.UserFromContext(ctx)
+
 	for _, action := range cfg.ActionButtons {
+		if !acl.IsAllowedView(cfg, user, &action) {
+			continue
+		}
+
 		btn := pb.ActionButton{
 			Id:    fmt.Sprintf("%x", md5.Sum([]byte(action.Title))),
 			Title: action.Title,
 			Icon:  lookupHTMLIcon(action.Icon),
+			CanExec: acl.IsAllowedExec(cfg, user, &action),
 		}
 
 		res.Actions = append(res.Actions, &btn)
 	}
 
-	log.Infof("getButtons: %v", res)
+	if len(res.Actions) == 0 {
+		log.Warn("Zero actions found - check that you have some actions defined, with a view permission")
+	}
+
+	log.Debugf("getButtons: %v", res)
 
 	return res, nil
 }