feat kill acl (#618)
This commit is contained in:
parent
b99b3f4345
commit
a8ac719af7
|
|
@ -138,6 +138,7 @@ message LogEntry {
|
||||||
bool execution_finished = 15;
|
bool execution_finished = 15;
|
||||||
bool blocked = 16;
|
bool blocked = 16;
|
||||||
int64 datetime_index = 17;
|
int64 datetime_index = 17;
|
||||||
|
bool can_kill = 18;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetLogsResponse {
|
message GetLogsResponse {
|
||||||
|
|
|
||||||
|
|
@ -1125,6 +1125,7 @@ type LogEntry struct {
|
||||||
ExecutionFinished bool `protobuf:"varint,15,opt,name=execution_finished,json=executionFinished,proto3" json:"execution_finished,omitempty"`
|
ExecutionFinished bool `protobuf:"varint,15,opt,name=execution_finished,json=executionFinished,proto3" json:"execution_finished,omitempty"`
|
||||||
Blocked bool `protobuf:"varint,16,opt,name=blocked,proto3" json:"blocked,omitempty"`
|
Blocked bool `protobuf:"varint,16,opt,name=blocked,proto3" json:"blocked,omitempty"`
|
||||||
DatetimeIndex int64 `protobuf:"varint,17,opt,name=datetime_index,json=datetimeIndex,proto3" json:"datetime_index,omitempty"`
|
DatetimeIndex int64 `protobuf:"varint,17,opt,name=datetime_index,json=datetimeIndex,proto3" json:"datetime_index,omitempty"`
|
||||||
|
CanKill bool `protobuf:"varint,18,opt,name=can_kill,json=canKill,proto3" json:"can_kill,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
@ -1271,6 +1272,13 @@ func (x *LogEntry) GetDatetimeIndex() int64 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *LogEntry) GetCanKill() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.CanKill
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type GetLogsResponse struct {
|
type GetLogsResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Logs []*LogEntry `protobuf:"bytes,1,rep,name=logs,proto3" json:"logs,omitempty"`
|
Logs []*LogEntry `protobuf:"bytes,1,rep,name=logs,proto3" json:"logs,omitempty"`
|
||||||
|
|
@ -2733,7 +2741,7 @@ const file_olivetin_api_v1_olivetin_proto_rawDesc = "" +
|
||||||
"\x1fStartActionByGetAndWaitResponse\x126\n" +
|
"\x1fStartActionByGetAndWaitResponse\x126\n" +
|
||||||
"\tlog_entry\x18\x01 \x01(\v2\x19.olivetin.api.v1.LogEntryR\blogEntry\"3\n" +
|
"\tlog_entry\x18\x01 \x01(\v2\x19.olivetin.api.v1.LogEntryR\blogEntry\"3\n" +
|
||||||
"\x0eGetLogsRequest\x12!\n" +
|
"\x0eGetLogsRequest\x12!\n" +
|
||||||
"\fstart_offset\x18\x01 \x01(\x03R\vstartOffset\"\xad\x04\n" +
|
"\fstart_offset\x18\x01 \x01(\x03R\vstartOffset\"\xc8\x04\n" +
|
||||||
"\bLogEntry\x12)\n" +
|
"\bLogEntry\x12)\n" +
|
||||||
"\x10datetime_started\x18\x01 \x01(\tR\x0fdatetimeStarted\x12!\n" +
|
"\x10datetime_started\x18\x01 \x01(\tR\x0fdatetimeStarted\x12!\n" +
|
||||||
"\faction_title\x18\x02 \x01(\tR\vactionTitle\x12\x16\n" +
|
"\faction_title\x18\x02 \x01(\tR\vactionTitle\x12\x16\n" +
|
||||||
|
|
@ -2753,7 +2761,8 @@ const file_olivetin_api_v1_olivetin_proto_rawDesc = "" +
|
||||||
"\x11execution_started\x18\x0e \x01(\bR\x10executionStarted\x12-\n" +
|
"\x11execution_started\x18\x0e \x01(\bR\x10executionStarted\x12-\n" +
|
||||||
"\x12execution_finished\x18\x0f \x01(\bR\x11executionFinished\x12\x18\n" +
|
"\x12execution_finished\x18\x0f \x01(\bR\x11executionFinished\x12\x18\n" +
|
||||||
"\ablocked\x18\x10 \x01(\bR\ablocked\x12%\n" +
|
"\ablocked\x18\x10 \x01(\bR\ablocked\x12%\n" +
|
||||||
"\x0edatetime_index\x18\x11 \x01(\x03R\rdatetimeIndex\"\x86\x01\n" +
|
"\x0edatetime_index\x18\x11 \x01(\x03R\rdatetimeIndex\x12\x19\n" +
|
||||||
|
"\bcan_kill\x18\x12 \x01(\bR\acanKill\"\x86\x01\n" +
|
||||||
"\x0fGetLogsResponse\x12-\n" +
|
"\x0fGetLogsResponse\x12-\n" +
|
||||||
"\x04logs\x18\x01 \x03(\v2\x19.olivetin.api.v1.LogEntryR\x04logs\x12'\n" +
|
"\x04logs\x18\x01 \x03(\v2\x19.olivetin.api.v1.LogEntryR\x04logs\x12'\n" +
|
||||||
"\x0fcount_remaining\x18\x02 \x01(\x03R\x0ecountRemaining\x12\x1b\n" +
|
"\x0fcount_remaining\x18\x02 \x01(\x03R\x0ecountRemaining\x12\x1b\n" +
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ const (
|
||||||
View PermissionBits = 1 << iota
|
View PermissionBits = 1 << iota
|
||||||
Exec
|
Exec
|
||||||
Logs
|
Logs
|
||||||
|
Kill
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p PermissionBits) Has(permission PermissionBits) bool {
|
func (p PermissionBits) Has(permission PermissionBits) bool {
|
||||||
|
|
@ -121,6 +122,10 @@ func permissionsConfigToBits(permissions config.PermissionsList) PermissionBits
|
||||||
ret |= Logs
|
ret |= Logs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if permissions.Kill {
|
||||||
|
ret |= Kill
|
||||||
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,6 +178,10 @@ func IsAllowedView(cfg *config.Config, user *AuthenticatedUser, action *config.A
|
||||||
return aclCheck(View, cfg.DefaultPermissions.View, cfg, "isAllowedView", user, action)
|
return aclCheck(View, cfg.DefaultPermissions.View, cfg, "isAllowedView", user, action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsAllowedKill(cfg *config.Config, user *AuthenticatedUser, action *config.Action) bool {
|
||||||
|
return aclCheck(Kill, cfg.DefaultPermissions.Kill, cfg, "isAllowedKill", user, action)
|
||||||
|
}
|
||||||
|
|
||||||
func getMetadataKeyOrEmpty(md metadata.MD, key string) string {
|
func getMetadataKeyOrEmpty(md metadata.MD, key string) string {
|
||||||
mdValues := md.Get(key)
|
mdValues := md.Get(key)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ type PermissionsList struct {
|
||||||
View bool
|
View bool
|
||||||
Exec bool
|
Exec bool
|
||||||
Logs bool
|
Logs bool
|
||||||
|
Kill bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccessControlList defines what permissions apply to a user or user group.
|
// AccessControlList defines what permissions apply to a user or user group.
|
||||||
|
|
@ -229,6 +230,7 @@ func DefaultConfigWithBasePort(basePort int) *Config {
|
||||||
config.DefaultPermissions.Exec = true
|
config.DefaultPermissions.Exec = true
|
||||||
config.DefaultPermissions.View = true
|
config.DefaultPermissions.View = true
|
||||||
config.DefaultPermissions.Logs = true
|
config.DefaultPermissions.Logs = true
|
||||||
|
config.DefaultPermissions.Kill = true
|
||||||
config.AuthJwtClaimUsername = "name"
|
config.AuthJwtClaimUsername = "name"
|
||||||
config.AuthJwtClaimUserGroup = "group"
|
config.AuthJwtClaimUserGroup = "group"
|
||||||
config.AuthRequireGuestsToLogin = false
|
config.AuthRequireGuestsToLogin = false
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,7 @@ func updateSvFromFile(entityname string, data []map[string]any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//gocyclo:ignore
|
||||||
func serializeValueToSv(prefix string, value any) {
|
func serializeValueToSv(prefix string, value any) {
|
||||||
if m, ok := value.(map[string]any); ok { // if value is a map we need to flatten it
|
if m, ok := value.(map[string]any); ok { // if value is a map we need to flatten it
|
||||||
serializeMapToSv(prefix, m)
|
serializeMapToSv(prefix, m)
|
||||||
|
|
|
||||||
|
|
@ -38,24 +38,40 @@ func (api *oliveTinAPI) KillAction(ctx ctx.Context, req *apiv1.KillActionRequest
|
||||||
ExecutionTrackingId: req.ExecutionTrackingId,
|
ExecutionTrackingId: req.ExecutionTrackingId,
|
||||||
}
|
}
|
||||||
|
|
||||||
execReqLogEntry, found := api.executor.GetLog(req.ExecutionTrackingId)
|
var execReqLogEntry *executor.InternalLogEntry
|
||||||
|
|
||||||
ret.Found = found
|
execReqLogEntry, ret.Found = api.executor.GetLog(req.ExecutionTrackingId)
|
||||||
|
|
||||||
if found {
|
if !ret.Found {
|
||||||
log.Warnf("Killing execution request by tracking ID: %v", req.ExecutionTrackingId)
|
|
||||||
|
|
||||||
err := api.executor.Kill(execReqLogEntry)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("Killing execution request err: %v", err)
|
|
||||||
ret.AlreadyCompleted = true
|
|
||||||
ret.Killed = false
|
|
||||||
} else {
|
|
||||||
ret.Killed = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Warnf("Killing execution request not possible - not found by tracking ID: %v", req.ExecutionTrackingId)
|
log.Warnf("Killing execution request not possible - not found by tracking ID: %v", req.ExecutionTrackingId)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnf("Killing execution request by tracking ID: %v", req.ExecutionTrackingId)
|
||||||
|
|
||||||
|
user := acl.UserFromContext(ctx, cfg)
|
||||||
|
action := cfg.FindAction(execReqLogEntry.ActionTitle)
|
||||||
|
|
||||||
|
if action == nil {
|
||||||
|
log.Warnf("Killing execution request not possible - action not found: %v", execReqLogEntry.ActionTitle)
|
||||||
|
ret.Killed = false
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !acl.IsAllowedKill(cfg, user, action) {
|
||||||
|
log.Warnf("Killing execution request not possible - user not allowed to kill this action: %v", req.ExecutionTrackingId)
|
||||||
|
ret.Killed = false
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := api.executor.Kill(execReqLogEntry)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Killing execution request err: %v", err)
|
||||||
|
ret.AlreadyCompleted = true
|
||||||
|
ret.Killed = false
|
||||||
|
} else {
|
||||||
|
ret.Killed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
@ -136,11 +152,13 @@ func (api *oliveTinAPI) StartActionAndWait(ctx ctx.Context, req *apiv1.StartActi
|
||||||
args[arg.Name] = arg.Value
|
args[arg.Name] = arg.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user := acl.UserFromContext(ctx, cfg)
|
||||||
|
|
||||||
execReq := executor.ExecutionRequest{
|
execReq := executor.ExecutionRequest{
|
||||||
Action: api.executor.FindActionBindingByID(req.ActionId),
|
Action: api.executor.FindActionBindingByID(req.ActionId),
|
||||||
TrackingID: uuid.NewString(),
|
TrackingID: uuid.NewString(),
|
||||||
Arguments: args,
|
Arguments: args,
|
||||||
AuthenticatedUser: acl.UserFromContext(ctx, cfg),
|
AuthenticatedUser: user,
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,7 +169,7 @@ func (api *oliveTinAPI) StartActionAndWait(ctx ctx.Context, req *apiv1.StartActi
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
return &apiv1.StartActionAndWaitResponse{
|
return &apiv1.StartActionAndWaitResponse{
|
||||||
LogEntry: internalLogEntryToPb(internalLogEntry),
|
LogEntry: internalLogEntryToPb(internalLogEntry, user),
|
||||||
}, nil
|
}, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("execution not found")
|
return nil, fmt.Errorf("execution not found")
|
||||||
|
|
@ -179,11 +197,13 @@ func (api *oliveTinAPI) StartActionByGet(ctx ctx.Context, req *apiv1.StartAction
|
||||||
func (api *oliveTinAPI) StartActionByGetAndWait(ctx ctx.Context, req *apiv1.StartActionByGetAndWaitRequest) (*apiv1.StartActionByGetAndWaitResponse, error) {
|
func (api *oliveTinAPI) StartActionByGetAndWait(ctx ctx.Context, req *apiv1.StartActionByGetAndWaitRequest) (*apiv1.StartActionByGetAndWaitResponse, error) {
|
||||||
args := make(map[string]string)
|
args := make(map[string]string)
|
||||||
|
|
||||||
|
user := acl.UserFromContext(ctx, cfg)
|
||||||
|
|
||||||
execReq := executor.ExecutionRequest{
|
execReq := executor.ExecutionRequest{
|
||||||
Action: api.executor.FindActionBindingByID(req.ActionId),
|
Action: api.executor.FindActionBindingByID(req.ActionId),
|
||||||
TrackingID: uuid.NewString(),
|
TrackingID: uuid.NewString(),
|
||||||
Arguments: args,
|
Arguments: args,
|
||||||
AuthenticatedUser: acl.UserFromContext(ctx, cfg),
|
AuthenticatedUser: user,
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,15 +214,15 @@ func (api *oliveTinAPI) StartActionByGetAndWait(ctx ctx.Context, req *apiv1.Star
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
return &apiv1.StartActionByGetAndWaitResponse{
|
return &apiv1.StartActionByGetAndWaitResponse{
|
||||||
LogEntry: internalLogEntryToPb(internalLogEntry),
|
LogEntry: internalLogEntryToPb(internalLogEntry, user),
|
||||||
}, nil
|
}, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, status.Errorf(codes.NotFound, "Execution not found.")
|
return nil, status.Errorf(codes.NotFound, "Execution not found.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func internalLogEntryToPb(logEntry *executor.InternalLogEntry) *apiv1.LogEntry {
|
func internalLogEntryToPb(logEntry *executor.InternalLogEntry, authenticatedUser *acl.AuthenticatedUser) *apiv1.LogEntry {
|
||||||
return &apiv1.LogEntry{
|
pble := &apiv1.LogEntry{
|
||||||
ActionTitle: logEntry.ActionTitle,
|
ActionTitle: logEntry.ActionTitle,
|
||||||
ActionIcon: logEntry.ActionIcon,
|
ActionIcon: logEntry.ActionIcon,
|
||||||
ActionId: logEntry.ActionId,
|
ActionId: logEntry.ActionId,
|
||||||
|
|
@ -219,6 +239,12 @@ func internalLogEntryToPb(logEntry *executor.InternalLogEntry) *apiv1.LogEntry {
|
||||||
ExecutionFinished: logEntry.ExecutionFinished,
|
ExecutionFinished: logEntry.ExecutionFinished,
|
||||||
User: logEntry.Username,
|
User: logEntry.Username,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !pble.ExecutionFinished {
|
||||||
|
pble.CanKill = acl.IsAllowedKill(cfg, authenticatedUser, cfg.FindAction(logEntry.ActionTitle))
|
||||||
|
}
|
||||||
|
|
||||||
|
return pble
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExecutionStatusByTrackingID(api *oliveTinAPI, executionTrackingId string) *executor.InternalLogEntry {
|
func getExecutionStatusByTrackingID(api *oliveTinAPI, executionTrackingId string) *executor.InternalLogEntry {
|
||||||
|
|
@ -249,6 +275,8 @@ func getMostRecentExecutionStatusById(api *oliveTinAPI, actionId string) *execut
|
||||||
func (api *oliveTinAPI) ExecutionStatus(ctx ctx.Context, req *apiv1.ExecutionStatusRequest) (*apiv1.ExecutionStatusResponse, error) {
|
func (api *oliveTinAPI) ExecutionStatus(ctx ctx.Context, req *apiv1.ExecutionStatusRequest) (*apiv1.ExecutionStatusResponse, error) {
|
||||||
res := &apiv1.ExecutionStatusResponse{}
|
res := &apiv1.ExecutionStatusResponse{}
|
||||||
|
|
||||||
|
user := acl.UserFromContext(ctx, cfg)
|
||||||
|
|
||||||
var ile *executor.InternalLogEntry
|
var ile *executor.InternalLogEntry
|
||||||
|
|
||||||
if req.ExecutionTrackingId != "" {
|
if req.ExecutionTrackingId != "" {
|
||||||
|
|
@ -261,7 +289,7 @@ func (api *oliveTinAPI) ExecutionStatus(ctx ctx.Context, req *apiv1.ExecutionSta
|
||||||
if ile == nil {
|
if ile == nil {
|
||||||
return nil, status.Error(codes.NotFound, "Execution not found")
|
return nil, status.Error(codes.NotFound, "Execution not found")
|
||||||
} else {
|
} else {
|
||||||
res.LogEntry = internalLogEntryToPb(ile)
|
res.LogEntry = internalLogEntryToPb(ile, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
@ -339,7 +367,7 @@ func (api *oliveTinAPI) GetLogs(ctx ctx.Context, req *apiv1.GetLogsRequest) (*ap
|
||||||
action := cfg.FindAction(logEntry.ActionTitle)
|
action := cfg.FindAction(logEntry.ActionTitle)
|
||||||
|
|
||||||
if action == nil || acl.IsAllowedLogs(cfg, user, action) {
|
if action == nil || acl.IsAllowedLogs(cfg, user, action) {
|
||||||
pbLogEntry := internalLogEntryToPb(logEntry)
|
pbLogEntry := internalLogEntryToPb(logEntry, user)
|
||||||
|
|
||||||
ret.Logs = append(ret.Logs, pbLogEntry)
|
ret.Logs = append(ret.Logs, pbLogEntry)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ export class ExecutionDialog {
|
||||||
this.domBtnRerun.disabled = !res.logEntry.executionFinished
|
this.domBtnRerun.disabled = !res.logEntry.executionFinished
|
||||||
this.domBtnRerun.onclick = () => { this.rerunAction(res.logEntry.actionId) }
|
this.domBtnRerun.onclick = () => { this.rerunAction(res.logEntry.actionId) }
|
||||||
|
|
||||||
this.domBtnKill.disabled = res.logEntry.executionFinished
|
this.domBtnKill.disabled = !res.logEntry.canKill
|
||||||
|
|
||||||
this.domStatus.update(res.logEntry)
|
this.domStatus.update(res.logEntry)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue