Initial work on auth

This commit is contained in:
jamesread 2021-07-19 14:22:57 +01:00
parent c072e16cc4
commit 5a151859d7
5 changed files with 144 additions and 15 deletions

View File

@ -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
internal/acl/acl.go Normal file
View File

@ -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",
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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
}