3
0

Implements script security, add on-manual sec. check

This commit is contained in:
Denis Arh
2020-01-22 07:46:49 +01:00
parent c26704b4cb
commit 9fd7adcaa2
14 changed files with 510 additions and 118 deletions
+1
View File
@@ -42,6 +42,7 @@ func (app *App) Setup(log *zap.Logger, opts *app.Options) (err error) {
// @todo Wire in cross-service JWT maker for Corredor
corredor.Service().SetUserFinder(nil)
corredor.Service().SetRoleFinder(nil)
return
}
+1
View File
@@ -23,6 +23,7 @@ func (Automation) New() *Automation {
func (ctrl *Automation) List(ctx context.Context, r *request.AutomationList) (interface{}, error) {
return corredor.GenericListHandler(
ctx,
corredor.Service(),
corredor.Filter{
ResourceTypes: r.ResourceTypes,
+1
View File
@@ -49,6 +49,7 @@ func (app *App) Setup(log *zap.Logger, opts *app.Options) (err error) {
// @todo Wire in cross-service JWT maker for Corredor
corredor.Service().SetUserFinder(nil)
corredor.Service().SetRoleFinder(nil)
return
}
+1
View File
@@ -71,6 +71,7 @@ func (monolith App) Initialize(ctx context.Context) (err error) {
}
corredor.Service().SetUserFinder(systemService.DefaultUser)
corredor.Service().SetRoleFinder(systemService.DefaultRole)
return
}
+4 -4
View File
@@ -6,10 +6,10 @@ import (
type (
Filter struct {
ResourceTypes []string
EventTypes []string
ExcludeServerScripts bool
ExcludeClientScripts bool
ResourceTypes []string `json:"resourceTypes"`
EventTypes []string `json:"eventTypes"`
ExcludeServerScripts bool `json:"excludeServerScripts"`
ExcludeClientScripts bool `json:"excludeClientScripts"`
Page uint `json:"page"`
PerPage uint `json:"perPage"`
+122 -64
View File
@@ -395,6 +395,7 @@ type ServerScript struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
Security *Security `protobuf:"bytes,13,opt,name=security,proto3" json:"security,omitempty"`
Triggers []*Trigger `protobuf:"bytes,14,rep,name=triggers,proto3" json:"triggers,omitempty"`
Errors []string `protobuf:"bytes,15,rep,name=errors,proto3" json:"errors,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -448,6 +449,13 @@ func (m *ServerScript) GetDescription() string {
return ""
}
func (m *ServerScript) GetSecurity() *Security {
if m != nil {
return m.Security
}
return nil
}
func (m *ServerScript) GetTriggers() []*Trigger {
if m != nil {
return m.Triggers
@@ -468,6 +476,7 @@ type ClientScript struct {
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
Bundle string `protobuf:"bytes,4,opt,name=bundle,proto3" json:"bundle,omitempty"`
Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"`
Security *Security `protobuf:"bytes,13,opt,name=security,proto3" json:"security,omitempty"`
Triggers []*Trigger `protobuf:"bytes,14,rep,name=triggers,proto3" json:"triggers,omitempty"`
Errors []string `protobuf:"bytes,15,rep,name=errors,proto3" json:"errors,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -535,6 +544,13 @@ func (m *ClientScript) GetType() string {
return ""
}
func (m *ClientScript) GetSecurity() *Security {
if m != nil {
return m.Security
}
return nil
}
func (m *ClientScript) GetTriggers() []*Trigger {
if m != nil {
return m.Triggers
@@ -549,12 +565,65 @@ func (m *ClientScript) GetErrors() []string {
return nil
}
type Security struct {
RunAs string `protobuf:"bytes,1,opt,name=runAs,proto3" json:"runAs,omitempty"`
Deny []string `protobuf:"bytes,2,rep,name=deny,proto3" json:"deny,omitempty"`
Allow []string `protobuf:"bytes,3,rep,name=allow,proto3" json:"allow,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Security) Reset() { *m = Security{} }
func (m *Security) String() string { return proto.CompactTextString(m) }
func (*Security) ProtoMessage() {}
func (*Security) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{10}
}
func (m *Security) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Security.Unmarshal(m, b)
}
func (m *Security) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Security.Marshal(b, m, deterministic)
}
func (m *Security) XXX_Merge(src proto.Message) {
xxx_messageInfo_Security.Merge(m, src)
}
func (m *Security) XXX_Size() int {
return xxx_messageInfo_Security.Size(m)
}
func (m *Security) XXX_DiscardUnknown() {
xxx_messageInfo_Security.DiscardUnknown(m)
}
var xxx_messageInfo_Security proto.InternalMessageInfo
func (m *Security) GetRunAs() string {
if m != nil {
return m.RunAs
}
return ""
}
func (m *Security) GetDeny() []string {
if m != nil {
return m.Deny
}
return nil
}
func (m *Security) GetAllow() []string {
if m != nil {
return m.Allow
}
return nil
}
type Trigger struct {
EventTypes []string `protobuf:"bytes,1,rep,name=eventTypes,proto3" json:"eventTypes,omitempty"`
ResourceTypes []string `protobuf:"bytes,2,rep,name=resourceTypes,proto3" json:"resourceTypes,omitempty"`
RunAs string `protobuf:"bytes,3,opt,name=runAs,proto3" json:"runAs,omitempty"`
Constraints []*TConstraint `protobuf:"bytes,4,rep,name=constraints,proto3" json:"constraints,omitempty"`
Ui []*TUIOption `protobuf:"bytes,5,rep,name=ui,proto3" json:"ui,omitempty"`
Constraints []*TConstraint `protobuf:"bytes,15,rep,name=constraints,proto3" json:"constraints,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -564,7 +633,7 @@ func (m *Trigger) Reset() { *m = Trigger{} }
func (m *Trigger) String() string { return proto.CompactTextString(m) }
func (*Trigger) ProtoMessage() {}
func (*Trigger) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{10}
return fileDescriptor_c06bb92bf45e37e2, []int{11}
}
func (m *Trigger) XXX_Unmarshal(b []byte) error {
@@ -599,13 +668,6 @@ func (m *Trigger) GetResourceTypes() []string {
return nil
}
func (m *Trigger) GetRunAs() string {
if m != nil {
return m.RunAs
}
return ""
}
func (m *Trigger) GetConstraints() []*TConstraint {
if m != nil {
return m.Constraints
@@ -613,13 +675,6 @@ func (m *Trigger) GetConstraints() []*TConstraint {
return nil
}
func (m *Trigger) GetUi() []*TUIOption {
if m != nil {
return m.Ui
}
return nil
}
type TConstraint struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Op string `protobuf:"bytes,2,opt,name=op,proto3" json:"op,omitempty"`
@@ -633,7 +688,7 @@ func (m *TConstraint) Reset() { *m = TConstraint{} }
func (m *TConstraint) String() string { return proto.CompactTextString(m) }
func (*TConstraint) ProtoMessage() {}
func (*TConstraint) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{11}
return fileDescriptor_c06bb92bf45e37e2, []int{12}
}
func (m *TConstraint) XXX_Unmarshal(b []byte) error {
@@ -687,7 +742,7 @@ func (m *TUIOption) Reset() { *m = TUIOption{} }
func (m *TUIOption) String() string { return proto.CompactTextString(m) }
func (*TUIOption) ProtoMessage() {}
func (*TUIOption) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{12}
return fileDescriptor_c06bb92bf45e37e2, []int{13}
}
func (m *TUIOption) XXX_Unmarshal(b []byte) error {
@@ -735,7 +790,7 @@ func (m *Bundle) Reset() { *m = Bundle{} }
func (m *Bundle) String() string { return proto.CompactTextString(m) }
func (*Bundle) ProtoMessage() {}
func (*Bundle) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{13}
return fileDescriptor_c06bb92bf45e37e2, []int{14}
}
func (m *Bundle) XXX_Unmarshal(b []byte) error {
@@ -790,6 +845,7 @@ func init() {
proto.RegisterType((*BundleResponse)(nil), "corredor.BundleResponse")
proto.RegisterType((*ServerScript)(nil), "corredor.ServerScript")
proto.RegisterType((*ClientScript)(nil), "corredor.ClientScript")
proto.RegisterType((*Security)(nil), "corredor.Security")
proto.RegisterType((*Trigger)(nil), "corredor.Trigger")
proto.RegisterType((*TConstraint)(nil), "corredor.TConstraint")
proto.RegisterType((*TUIOption)(nil), "corredor.TUIOption")
@@ -799,49 +855,51 @@ func init() {
func init() { proto.RegisterFile("service-corredor.proto", fileDescriptor_c06bb92bf45e37e2) }
var fileDescriptor_c06bb92bf45e37e2 = []byte{
// 666 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xcd, 0x6e, 0x13, 0x31,
0x10, 0xd6, 0x6e, 0x36, 0x49, 0x33, 0x49, 0x4a, 0x31, 0x6d, 0x6a, 0xe5, 0x00, 0xc1, 0xe5, 0x50,
0x21, 0x35, 0x42, 0xa9, 0xaa, 0x42, 0xe1, 0xd2, 0x96, 0x0a, 0x21, 0x15, 0x21, 0x6d, 0xcb, 0x03,
0xa4, 0x9b, 0x51, 0xb4, 0x22, 0xac, 0x53, 0xdb, 0x1b, 0xd1, 0x07, 0xe0, 0xc4, 0x81, 0x13, 0x27,
0x4e, 0x3c, 0x0b, 0x67, 0xde, 0x09, 0xad, 0xed, 0xdd, 0xb8, 0xdd, 0xa4, 0x02, 0xa9, 0xe2, 0xe6,
0x19, 0x7f, 0x33, 0xf3, 0xcd, 0x8f, 0xc7, 0xd0, 0x91, 0x28, 0x66, 0x71, 0x84, 0x3b, 0x11, 0x17,
0x02, 0x47, 0x5c, 0xf4, 0xa7, 0x82, 0x2b, 0x4e, 0x56, 0x72, 0x99, 0x7d, 0xf3, 0xa0, 0x79, 0xf2,
0x19, 0xa3, 0x10, 0x2f, 0x53, 0x94, 0x8a, 0x10, 0x08, 0x92, 0xe1, 0x27, 0xa4, 0x5e, 0xcf, 0xdb,
0x6e, 0x84, 0xfa, 0x4c, 0x76, 0x21, 0x18, 0x8a, 0xb1, 0xa4, 0x7e, 0xaf, 0xb2, 0xdd, 0x1c, 0x3c,
0xea, 0x17, 0xce, 0x1c, 0xc3, 0xfe, 0xa1, 0x18, 0xcb, 0x93, 0x44, 0x89, 0xab, 0x50, 0x83, 0xbb,
0xfb, 0xd0, 0x28, 0x54, 0x64, 0x0d, 0x2a, 0x1f, 0xf1, 0xca, 0x3a, 0xcd, 0x8e, 0x64, 0x1d, 0xaa,
0xb3, 0xe1, 0x24, 0x45, 0xea, 0x6b, 0x9d, 0x11, 0x0e, 0xfc, 0xe7, 0x1e, 0xfb, 0xe2, 0x41, 0xcb,
0x38, 0x96, 0x53, 0x9e, 0x48, 0x24, 0x07, 0x50, 0x13, 0x28, 0xd3, 0x89, 0xb2, 0x04, 0xd8, 0x4d,
0x02, 0x06, 0xd7, 0x0f, 0x35, 0xc8, 0x70, 0xb0, 0x16, 0xdd, 0x17, 0xd0, 0x74, 0xd4, 0xff, 0xc4,
0x43, 0xc2, 0xe6, 0x19, 0x8a, 0x19, 0x8a, 0xb3, 0x48, 0xc4, 0x53, 0x75, 0x1a, 0x4b, 0x95, 0x17,
0x69, 0x1d, 0xaa, 0x97, 0x29, 0x8a, 0xdc, 0x91, 0x11, 0x08, 0x83, 0x96, 0x40, 0xc9, 0x53, 0x11,
0xe1, 0xf9, 0xd5, 0x34, 0xf7, 0x78, 0x4d, 0x47, 0x1e, 0x02, 0xe0, 0x0c, 0x13, 0x95, 0x09, 0x92,
0x56, 0x7a, 0x95, 0xed, 0x46, 0xe8, 0x68, 0xd8, 0x29, 0xd0, 0x72, 0x50, 0x5b, 0x87, 0x67, 0x50,
0x97, 0x5a, 0x2b, 0xa9, 0xa7, 0x0b, 0xd1, 0x99, 0x17, 0xc2, 0x35, 0x0a, 0x73, 0x18, 0xfb, 0xea,
0xc1, 0xe6, 0xf1, 0x24, 0xc6, 0x44, 0xfd, 0xc7, 0x1c, 0x48, 0x07, 0x6a, 0x17, 0x69, 0x32, 0x9a,
0x20, 0x0d, 0xb4, 0xb5, 0x95, 0xb2, 0xdc, 0xca, 0x64, 0xfe, 0x22, 0x37, 0xd7, 0x68, 0x9e, 0xdb,
0x16, 0xb4, 0x8f, 0xb4, 0xdf, 0x5b, 0x26, 0x97, 0xbd, 0x82, 0xd5, 0x1c, 0x64, 0x03, 0x3d, 0x85,
0xba, 0xa1, 0x93, 0x07, 0x5a, 0x9b, 0x07, 0xb2, 0xd0, 0x1c, 0xc0, 0x7e, 0x7a, 0xd0, 0x72, 0x0b,
0xbb, 0xf0, 0x71, 0xac, 0x43, 0x75, 0x32, 0xbc, 0xc0, 0x49, 0x3e, 0x40, 0x5a, 0x20, 0x3d, 0x68,
0x8e, 0xd0, 0x50, 0x8d, 0x79, 0x42, 0x2b, 0xfa, 0xce, 0x55, 0x91, 0x1d, 0x58, 0x51, 0x22, 0x1e,
0x8f, 0x51, 0x48, 0xba, 0xaa, 0x99, 0xdc, 0x9f, 0x33, 0x39, 0x37, 0x37, 0x61, 0x01, 0xc9, 0x8a,
0x8a, 0x42, 0x70, 0x21, 0xe9, 0x3d, 0x5d, 0x70, 0x2b, 0xb1, 0xdf, 0x1e, 0xb4, 0xdc, 0x02, 0xdd,
0x29, 0xc7, 0x25, 0x9d, 0xcc, 0x62, 0xa8, 0x6c, 0x3a, 0x6a, 0x26, 0x46, 0x76, 0xbe, 0xab, 0x7c,
0x7e, 0x79, 0x50, 0xb7, 0xe8, 0x1b, 0x83, 0xe6, 0x95, 0x06, 0xed, 0x09, 0xb4, 0xdd, 0xc1, 0x34,
0x0b, 0xaa, 0x11, 0x5e, 0x57, 0x66, 0xc9, 0x8b, 0x34, 0x39, 0x94, 0x36, 0x41, 0x23, 0x90, 0x7d,
0x68, 0x46, 0x3c, 0x91, 0x4a, 0x0c, 0xe3, 0x44, 0x49, 0x1a, 0x68, 0xc6, 0x1b, 0x0e, 0xe3, 0xe3,
0xe2, 0x36, 0x74, 0x91, 0x64, 0x0b, 0xfc, 0x34, 0xa6, 0x55, 0x8d, 0x7f, 0xe0, 0xe0, 0x3f, 0xbc,
0x7d, 0xaf, 0x8b, 0x16, 0xfa, 0x69, 0xcc, 0xde, 0x40, 0xd3, 0x71, 0xb0, 0xb0, 0x27, 0xab, 0xe0,
0xf3, 0xa9, 0x6d, 0x88, 0xcf, 0xa7, 0xf3, 0x45, 0x64, 0x1e, 0x94, 0x11, 0xd8, 0x1e, 0x34, 0x0a,
0xcf, 0xcb, 0x5a, 0x5b, 0xde, 0x5f, 0xec, 0x35, 0xd4, 0x8e, 0x8a, 0x56, 0x95, 0x6c, 0xf2, 0xf6,
0xf9, 0x4e, 0xfb, 0x08, 0x04, 0x11, 0x1f, 0xa1, 0x2d, 0x92, 0x3e, 0x0f, 0xbe, 0x7b, 0xd0, 0x76,
0xe7, 0x5f, 0x92, 0x3d, 0x08, 0xb2, 0x95, 0x4b, 0x36, 0x16, 0xfe, 0x01, 0xdd, 0xce, 0xe2, 0xcd,
0x4c, 0xde, 0x41, 0x90, 0xbd, 0x76, 0xf2, 0x78, 0xf1, 0xc2, 0x72, 0xd6, 0x52, 0x97, 0xdd, 0x06,
0x31, 0xee, 0x06, 0x3f, 0x3c, 0x68, 0xbb, 0x33, 0x2f, 0xc9, 0xcb, 0x22, 0xdf, 0xcd, 0xd2, 0x73,
0xb6, 0x8e, 0x69, 0xf9, 0x62, 0x39, 0xbb, 0x25, 0x4b, 0xd3, 0x65, 0xb7, 0x6c, 0x95, 0x0d, 0x1a,
0x50, 0x3f, 0x53, 0x5c, 0x0c, 0xc7, 0x78, 0x51, 0xd3, 0xbf, 0xed, 0xee, 0x9f, 0x00, 0x00, 0x00,
0xff, 0xff, 0xc9, 0x23, 0xe5, 0x0a, 0x87, 0x07, 0x00, 0x00,
// 695 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x55, 0xdd, 0x6a, 0x13, 0x41,
0x14, 0x66, 0x92, 0x34, 0x3f, 0x67, 0x93, 0x5a, 0x87, 0x36, 0x5d, 0x72, 0xa1, 0x71, 0xf4, 0x22,
0x08, 0x0d, 0x92, 0x52, 0xaa, 0xd5, 0x9b, 0xb6, 0x16, 0x51, 0x2a, 0xc2, 0xb6, 0x3e, 0x40, 0xba,
0x39, 0x84, 0xc5, 0x75, 0x27, 0x9d, 0x99, 0x8d, 0xe6, 0x01, 0x04, 0xc1, 0x0b, 0xaf, 0xbc, 0xf2,
0xa9, 0x7c, 0x20, 0x41, 0x76, 0x66, 0x36, 0x99, 0x76, 0x93, 0xa2, 0x20, 0x7a, 0x37, 0xe7, 0xcc,
0x37, 0xe7, 0x7c, 0xe7, 0x77, 0xa0, 0x2d, 0x51, 0x4c, 0xa3, 0x10, 0x77, 0x42, 0x2e, 0x04, 0x8e,
0xb8, 0xe8, 0x4f, 0x04, 0x57, 0x9c, 0xd6, 0x73, 0x99, 0x7d, 0x25, 0xe0, 0x9d, 0x7c, 0xc4, 0x30,
0xc0, 0xcb, 0x14, 0xa5, 0xa2, 0x14, 0x2a, 0xc9, 0xf0, 0x3d, 0xfa, 0xa4, 0x4b, 0x7a, 0x8d, 0x40,
0x9f, 0xe9, 0x2e, 0x54, 0x86, 0x62, 0x2c, 0xfd, 0x52, 0xb7, 0xdc, 0xf3, 0x06, 0x77, 0xfb, 0x73,
0x63, 0xce, 0xc3, 0xfe, 0xa1, 0x18, 0xcb, 0x93, 0x44, 0x89, 0x59, 0xa0, 0xc1, 0x9d, 0x7d, 0x68,
0xcc, 0x55, 0x74, 0x03, 0xca, 0xef, 0x70, 0x66, 0x8d, 0x66, 0x47, 0xba, 0x09, 0x6b, 0xd3, 0x61,
0x9c, 0xa2, 0x5f, 0xd2, 0x3a, 0x23, 0x1c, 0x94, 0x1e, 0x13, 0xf6, 0x89, 0x40, 0xd3, 0x18, 0x96,
0x13, 0x9e, 0x48, 0xa4, 0x07, 0x50, 0x15, 0x28, 0xd3, 0x58, 0x59, 0x02, 0xec, 0x3a, 0x01, 0x83,
0xeb, 0x07, 0x1a, 0x64, 0x38, 0xd8, 0x17, 0x9d, 0x27, 0xe0, 0x39, 0xea, 0x3f, 0xe2, 0x21, 0x61,
0xfb, 0x0c, 0xc5, 0x14, 0xc5, 0x59, 0x28, 0xa2, 0x89, 0x3a, 0x8d, 0xa4, 0xca, 0x93, 0xb4, 0x09,
0x6b, 0x97, 0x29, 0x8a, 0xdc, 0x90, 0x11, 0x28, 0x83, 0xa6, 0x40, 0xc9, 0x53, 0x11, 0xe2, 0xf9,
0x6c, 0x92, 0x5b, 0xbc, 0xa2, 0xa3, 0x77, 0x00, 0x70, 0x8a, 0x89, 0xca, 0x04, 0xe9, 0x97, 0xbb,
0xe5, 0x5e, 0x23, 0x70, 0x34, 0xec, 0x14, 0xfc, 0xa2, 0x53, 0x9b, 0x87, 0x47, 0x50, 0x93, 0x5a,
0x2b, 0x7d, 0xa2, 0x13, 0xd1, 0x5e, 0x24, 0xc2, 0x7d, 0x14, 0xe4, 0x30, 0xf6, 0x85, 0xc0, 0xf6,
0x71, 0x1c, 0x61, 0xa2, 0xfe, 0x61, 0x0c, 0xb4, 0x0d, 0xd5, 0x8b, 0x34, 0x19, 0xc5, 0xe8, 0x57,
0xf4, 0x6b, 0x2b, 0x65, 0xb1, 0x15, 0xc9, 0xfc, 0x46, 0x6c, 0xee, 0xa3, 0x45, 0x6c, 0xf7, 0xa1,
0x75, 0xa4, 0xed, 0xde, 0xd0, 0xb9, 0xec, 0x19, 0xac, 0xe7, 0x20, 0xeb, 0xe8, 0x21, 0xd4, 0x0c,
0x9d, 0xdc, 0xd1, 0xc6, 0xc2, 0x91, 0x85, 0xe6, 0x00, 0xf6, 0x83, 0x40, 0xd3, 0x4d, 0xec, 0xd2,
0xe1, 0xd8, 0x84, 0xb5, 0x78, 0x78, 0x81, 0x71, 0xde, 0x40, 0x5a, 0xa0, 0x5d, 0xf0, 0x46, 0x68,
0xa8, 0x46, 0x3c, 0xf1, 0xcb, 0xfa, 0xce, 0x55, 0xd1, 0x3e, 0xd4, 0x25, 0x86, 0xa9, 0x88, 0xd4,
0xcc, 0x6f, 0x75, 0x49, 0xcf, 0x1b, 0x50, 0xb7, 0x9c, 0xe6, 0x26, 0x98, 0x63, 0xe8, 0x0e, 0xd4,
0x95, 0x88, 0xc6, 0x63, 0x14, 0xd2, 0x5f, 0xd7, 0xcc, 0x6f, 0x2f, 0xf0, 0xe7, 0xe6, 0x26, 0x98,
0x43, 0xb2, 0x22, 0xa0, 0x10, 0x5c, 0x48, 0xff, 0x96, 0x2e, 0x90, 0x95, 0xd8, 0x4f, 0x02, 0x4d,
0x37, 0xa1, 0x7f, 0x35, 0xa6, 0x15, 0x95, 0xcf, 0x7c, 0xa8, 0xac, 0x9b, 0xaa, 0xc6, 0x47, 0x76,
0xfe, 0x5f, 0xf1, 0xbf, 0x82, 0x7a, 0x6e, 0x3c, 0x0b, 0x53, 0xa4, 0xc9, 0xa1, 0xcc, 0x47, 0x40,
0x0b, 0x19, 0xd9, 0x11, 0x26, 0x33, 0xbd, 0x6c, 0x1a, 0x81, 0x3e, 0x67, 0xc8, 0x61, 0x1c, 0xf3,
0x0f, 0xb6, 0xdb, 0x8d, 0xc0, 0x3e, 0x13, 0xa8, 0x59, 0xcf, 0xd7, 0x86, 0x82, 0x14, 0x86, 0xe2,
0x01, 0xb4, 0xdc, 0x21, 0x92, 0xd6, 0xfc, 0x55, 0x25, 0xdd, 0x07, 0x2f, 0xe4, 0x89, 0x54, 0x62,
0x18, 0x25, 0xca, 0x50, 0xf7, 0x06, 0x5b, 0x4e, 0x9c, 0xc7, 0xf3, 0xdb, 0xc0, 0x45, 0xb2, 0x17,
0xe0, 0x39, 0x77, 0x4b, 0x8b, 0xba, 0x0e, 0x25, 0x3e, 0xb1, 0x15, 0x2d, 0xf1, 0xc9, 0x62, 0xf3,
0xd9, 0x98, 0xb4, 0xc0, 0xf6, 0xa0, 0x71, 0xfe, 0xf6, 0xe5, 0x1b, 0x53, 0xcf, 0x15, 0xbd, 0x51,
0x5c, 0x98, 0xec, 0x39, 0x54, 0x8f, 0xe6, 0xb5, 0x2e, 0xbc, 0xc9, 0xeb, 0x5f, 0x72, 0xea, 0x4f,
0xa1, 0x12, 0xf2, 0x11, 0xda, 0x36, 0xd2, 0xe7, 0xc1, 0x37, 0x02, 0x2d, 0x77, 0xe0, 0x24, 0xdd,
0x83, 0x4a, 0xb6, 0xe3, 0xe9, 0xd6, 0xd2, 0x4f, 0xa7, 0xd3, 0x5e, 0xfe, 0x15, 0xd0, 0xd7, 0x50,
0xc9, 0xd6, 0x0b, 0xbd, 0xb7, 0x7c, 0x43, 0x3a, 0x7b, 0xb0, 0xc3, 0x6e, 0x82, 0x18, 0x73, 0x83,
0xef, 0x04, 0x5a, 0xee, 0xd0, 0x48, 0xfa, 0x74, 0x1e, 0xef, 0x76, 0x61, 0x7f, 0x58, 0xc3, 0x7e,
0xf1, 0x62, 0x35, 0xbb, 0x15, 0x5b, 0xda, 0x65, 0xb7, 0x6a, 0x77, 0x0e, 0x1a, 0x50, 0x3b, 0x53,
0x5c, 0x0c, 0xc7, 0x78, 0x51, 0xd5, 0xdf, 0xfb, 0xee, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f,
0x1d, 0x79, 0x1c, 0xf8, 0x07, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
+192 -30
View File
@@ -2,7 +2,7 @@ package corredor
import (
"context"
"github.com/cortezaproject/corteza-server/pkg/permissions"
"github.com/go-chi/chi/middleware"
"github.com/pkg/errors"
"go.uber.org/zap"
@@ -28,8 +28,8 @@ type (
registered map[string][]uintptr
// list of all registered onManual triggers & scripts
// map[<script-name>][<resource>] = <run-as>
manual map[string]map[string]string
// map[<script-name>][<resource>] = true
manual map[string]map[string]bool
// Combined list of client and server scripts
sScripts ScriptSet
@@ -44,7 +44,14 @@ type (
eventRegistry eventRegistry
authTokenMaker authTokenMaker
users userFinder
// Services to help with script security
// we'll find users (runAs) and roles (allow, deny) for
users userFinder
roles roleFinder
// set of permission rules, generated from security info of each script
permissions permissions.RuleSet
}
Event interface {
@@ -67,9 +74,17 @@ type (
FindByAny(interface{}) (*types.User, error)
}
roleFinder interface {
FindByAny(interface{}) (*types.Role, error)
}
authTokenMaker interface {
Encode(auth.Identifiable) string
}
permissionRuleChecker interface {
Check(res permissions.Resource, op permissions.Operation, roles ...uint64) permissions.Access
}
)
const onManualEventType = "onManual"
@@ -79,6 +94,10 @@ var (
gCorredor *service
)
const (
permOpExec permissions.Operation = "exec"
)
func Service() *service {
return gCorredor
}
@@ -100,10 +119,11 @@ func NewService(logger *zap.Logger, opt options.CorredorOpt) *service {
opt: opt,
registered: make(map[string][]uintptr),
manual: make(map[string]map[string]string),
manual: make(map[string]map[string]bool),
authTokenMaker: auth.DefaultJwtHandler,
eventRegistry: eventbus.Service(),
permissions: permissions.RuleSet{},
}
}
@@ -138,19 +158,23 @@ func (svc *service) SetUserFinder(uf userFinder) {
svc.users = uf
}
func (svc *service) SetRoleFinder(rf roleFinder) {
svc.roles = rf
}
func (svc *service) Load(ctx context.Context) {
go svc.loadServerScripts(ctx)
go svc.loadClientScripts(ctx)
}
// FindManual returns filtered list of scripts that can be manually triggered
func (svc service) Find(filter Filter) (out ScriptSet, f Filter, err error) {
// Find returns filtered list of scripts that can be manually triggered
func (svc service) Find(ctx context.Context, filter Filter) (out ScriptSet, f Filter, err error) {
f = filter
var (
tmp ScriptSet
scriptFilter = makeScriptFilter(f)
scriptFilter = svc.makeScriptFilter(ctx, f)
)
if !f.ExcludeServerScripts {
@@ -166,12 +190,32 @@ func (svc service) Find(filter Filter) (out ScriptSet, f Filter, err error) {
return
}
// ExecOnManual verifies request & executes
func (svc service) ExecOnManual(ctx context.Context, script string, event Event) (err error) {
// An enhanced version of basic script filter maker (from util.go)
// that (after basic filtering) also does RBAC check for each script
func (svc service) makeScriptFilter(ctx context.Context, f Filter) func(s *Script) (b bool, err error) {
var (
base = makeScriptFilter(f)
)
return func(s *Script) (b bool, err error) {
if b, err = base(s); !b {
return
}
b = svc.canExec(ctx, s.Name)
return
}
}
// ExecOnManual verifies permissions, event and script and sends exec request to corredor
func (svc service) ExecOnManual(ctx context.Context, scriptName string, event Event) (err error) {
var (
res = event.ResourceType()
evt = event.EventType()
script *Script
ok bool
runAs string
)
@@ -180,15 +224,37 @@ func (svc service) ExecOnManual(ctx context.Context, script string, event Event)
return errors.Errorf("triggered event type is not onManual (%q)", evt)
}
if _, ok = svc.manual[script]; !ok {
return errors.Errorf("unregistered onManual script %q", script)
if len(scriptName) == 0 {
return errors.Errorf("script name not provided (%q)", scriptName)
}
if runAs, ok = svc.manual[script][res]; !ok {
return errors.Errorf("unregistered onManual script %q for resource %q", script, res)
if _, ok = svc.manual[scriptName]; !ok {
return errors.Errorf("unregistered onManual script %q", scriptName)
}
return svc.exec(ctx, script, runAs, event)
if _, ok = svc.manual[scriptName][res]; !ok {
return errors.Errorf("unregistered onManual script %q for resource %q", scriptName, res)
}
if script = svc.sScripts.FindByName(scriptName); script == nil {
return errors.Errorf("nonexistent script (%q)", scriptName)
}
if !svc.canExec(ctx, scriptName) {
return errors.Errorf("permission to execute %s denied", scriptName)
}
return svc.exec(ctx, scriptName, runAs, event)
}
// Can current user execute this script
func (svc service) canExec(ctx context.Context, script string) bool {
u := auth.GetIdentityFromContext(ctx)
if auth.IsSuperUser(u) {
return true
}
return svc.permissions.Check(permissions.Resource(script), permOpExec, u.Roles()...) != permissions.Deny
}
func (svc *service) loadServerScripts(ctx context.Context) {
@@ -213,7 +279,31 @@ func (svc *service) loadServerScripts(ctx context.Context) {
// Registers Corredor scripts to eventbus and list of manual scripts
func (svc *service) registerServerScripts(ss ...*ServerScript) {
svc.sScripts = make([]*Script, len(ss))
var (
permRuleGenerator = func(script string, access permissions.Access, roles ...string) (permissions.RuleSet, error) {
out := make([]*permissions.Rule, len(roles))
for i, role := range roles {
if r, err := svc.roles.FindByAny(role); err != nil {
return nil, err
} else {
out[i] = &permissions.Rule{
RoleID: r.ID,
Resource: permissions.Resource(script),
Operation: permOpExec,
Access: access,
}
}
}
return out, nil
}
u *types.User
err error
runAs = ""
)
svc.sScripts = make([]*Script, 0, len(ss))
// Remove all previously registered triggers
for _, ptrs := range svc.registered {
@@ -224,28 +314,94 @@ func (svc *service) registerServerScripts(ss ...*ServerScript) {
// Reset indexes
svc.registered = make(map[string][]uintptr)
svc.manual = make(map[string]map[string]string)
svc.manual = make(map[string]map[string]bool)
for i, script := range ss {
svc.sScripts[i] = &Script{
// Reset security
svc.permissions = permissions.RuleSet{}
for _, script := range ss {
var (
// collectors for allow&deny rules
// we'll merge
allow = permissions.RuleSet{}
deny = permissions.RuleSet{}
)
if nil != svc.sScripts.FindByName(script.Name) {
// Do not allow duplicated scripts
continue
}
s := &Script{
Name: script.Name,
Label: script.Label,
Description: script.Description,
Errors: script.Errors,
Triggers: script.Triggers,
Security: &ScriptSecurity{Security: script.Security},
}
if len(script.Errors) == 0 {
svc.manual[script.Name] = pluckManualTriggers(script)
svc.registered[script.Name] = svc.registerTriggers(script)
scriptErrPush := func(err error, msg string) {
s.Errors = append(s.Errors, errors.Wrap(err, msg).Error())
}
svc.log.Debug(
"registered",
zap.String("script", script.Name),
zap.Int("manual", len(svc.manual[script.Name])),
zap.Int("triggers", len(svc.registered[script.Name])),
)
if len(s.Errors) == 0 {
if manual := pluckManualTriggers(script); len(manual) > 0 {
if script.Security != nil {
runAs = script.Security.RunAs
if runAs != "" {
// Prefetch run-as user
if u, err = svc.users.FindByAny(runAs); err != nil {
scriptErrPush(err, "could not load run-as user security info")
} else {
s.Security.runAs = u.ID
}
}
if allow, err = permRuleGenerator(script.Name, permissions.Allow, script.Security.Allow...); err != nil {
scriptErrPush(err, "could not load allow role security info")
}
if deny, err = permRuleGenerator(script.Name, permissions.Deny, script.Security.Deny...); err != nil {
scriptErrPush(err, "could not load deny role security info")
}
svc.permissions = append(svc.permissions, allow...)
svc.permissions = append(svc.permissions, deny...)
}
if len(s.Errors) == 0 {
svc.manual[script.Name] = manual
}
}
if len(s.Errors) == 0 {
svc.registered[script.Name] = svc.registerTriggers(script)
}
}
// Even if there are errors, we'll append the scripts
// because we need to serve them as a list for script management
svc.sScripts = append(svc.sScripts, s)
if len(s.Errors) == 0 {
svc.log.Debug(
"script registered",
zap.String("script", s.Name),
zap.Stringer("security", s.Security),
zap.Int("manual", len(svc.manual[script.Name])),
zap.Int("triggers", len(svc.registered[script.Name])),
)
} else {
svc.log.Warn(
"script loaded with errors",
zap.String("script", s.Name),
zap.Strings("errors", s.Errors),
)
}
}
}
@@ -260,8 +416,14 @@ func (svc *service) registerTriggers(script *ServerScript) []uintptr {
ptrs = make([]uintptr, 0, len(script.Triggers))
log = svc.log.With(zap.String("script", script.Name))
runAs string
)
if script.Security != nil {
runAs = script.Security.RunAs
}
for i := range script.Triggers {
// We're modifying trigger in the loop,
// so let's make a copy we can play with
@@ -286,9 +448,9 @@ func (svc *service) registerTriggers(script *ServerScript) []uintptr {
ptr := svc.eventRegistry.Register(func(ctx context.Context, ev eventbus.Event) (err error) {
// Is this compatible event?
if cce, ok := ev.(Event); ok {
if ev, ok := ev.(Event); ok {
// Can only work with corteza compatible events
return svc.exec(ctx, script.Name, trigger.RunAs, cce)
return svc.exec(ctx, script.Name, runAs, ev)
}
return nil
+99 -4
View File
@@ -1,12 +1,16 @@
package corredor
import (
"context"
"github.com/cortezaproject/corteza-server/pkg/app/options"
"github.com/cortezaproject/corteza-server/pkg/auth"
"github.com/cortezaproject/corteza-server/pkg/eventbus"
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/cortezaproject/corteza-server/pkg/permissions"
"github.com/cortezaproject/corteza-server/system/types"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"testing"
"github.com/stretchr/testify/assert"
)
type (
@@ -15,6 +19,15 @@ type (
eType string
match func(matcher eventbus.ConstraintMatcher) bool
}
mockUserSvc struct {
user *types.User
err error
}
mockRoleSvc struct {
role *types.Role
err error
}
)
func (e mockEvent) ResourceType() string {
@@ -41,11 +54,23 @@ func (e mockEvent) Match(matcher eventbus.ConstraintMatcher) bool {
return e.match(matcher)
}
func (u *mockUserSvc) FindByAny(interface{}) (*types.User, error) {
return u.user, u.err
}
func (u *mockRoleSvc) FindByAny(interface{}) (*types.Role, error) {
return u.role, u.err
}
func TestFindOnManual(t *testing.T) {
var (
ctx = context.Background()
svc = &service{
permissions: permissions.RuleSet{},
sScripts: ScriptSet{
&Script{
Name: "s1",
Triggers: []*Trigger{
&Trigger{
EventTypes: []string{"ev"},
@@ -54,6 +79,7 @@ func TestFindOnManual(t *testing.T) {
},
},
&Script{
Name: "s2",
Triggers: []*Trigger{
&Trigger{
EventTypes: []string{"foo"},
@@ -72,6 +98,7 @@ func TestFindOnManual(t *testing.T) {
},
cScripts: ScriptSet{
&Script{
Name: "s3",
Triggers: []*Trigger{
&Trigger{
EventTypes: []string{"ev"},
@@ -80,6 +107,7 @@ func TestFindOnManual(t *testing.T) {
},
},
&Script{
Name: "s4",
Triggers: []*Trigger{
&Trigger{
EventTypes: []string{"foo"},
@@ -96,7 +124,7 @@ func TestFindOnManual(t *testing.T) {
ExcludeClientScripts: false,
}
o, _, err = svc.Find(filter)
o, _, err = svc.Find(ctx, filter)
a = assert.New(t)
)
@@ -129,13 +157,80 @@ func TestServiceBasics(t *testing.T) {
svc.SetEventRegistry(nil)
svc.SetAuthTokenMaker(nil)
svc.SetUserFinder(nil)
svc.SetRoleFinder(nil)
}
func TestService_canExec(t *testing.T) {
var (
a = assert.New(t)
svc = &service{
users: &mockUserSvc{user: &types.User{ID: 42, Email: "dummy@mo.ck", Handle: "dummy"}},
roles: &mockRoleSvc{role: &types.Role{ID: 84, Handle: "role", Name: "ROLE"}},
permissions: permissions.RuleSet{},
}
ctx = auth.SetIdentityToContext(context.Background(), auth.NewIdentity(42, 84))
script1 = &ServerScript{
Name: "s1",
Triggers: []*Trigger{&Trigger{
EventTypes: []string{onManualEventType},
ResourceTypes: []string{"res"},
}},
Security: &Security{
RunAs: "foo",
Allow: []string{"role"},
},
}
script2 = &ServerScript{
Name: "s2",
Triggers: []*Trigger{&Trigger{
EventTypes: []string{onManualEventType},
ResourceTypes: []string{"res"},
}},
Security: &Security{
RunAs: "foo",
Deny: []string{"role"},
},
}
script3 = &ServerScript{
Name: "s3", // permissions will not be added, not a manual script
Security: &Security{
RunAs: "foo",
Deny: []string{"role"},
},
}
script4 = &ServerScript{
Name: "s3", // should not be even added, duplicated name (hence length=3)
Security: &Security{
RunAs: "foo",
Deny: []string{"role"},
},
}
)
if testing.Verbose() {
svc.log = logger.MakeDebugLogger()
} else {
svc.log = zap.NewNop()
}
svc.registerServerScripts(script1, script2, script3, script4)
a.Len(svc.sScripts, 3)
a.Len(svc.permissions, 2)
a.True(svc.canExec(ctx, script1.Name))
a.False(svc.canExec(ctx, script2.Name))
}
func TestService_ExecOnManual(t *testing.T) {
var (
a = assert.New(t)
svc = &service{
manual: map[string]map[string]string{},
manual: map[string]map[string]bool{},
}
)
+51 -5
View File
@@ -1,12 +1,33 @@
package corredor
import (
"fmt"
"strings"
)
type (
// ScriptSecurity sets script security and run-as flag
//
// Determinates user (if >0) that script will run under
// and allow/deny combination for roles that can see & execute
// explicit (onManual) scripts
//
// Please note that implicit scripts cannot have RBAC protection!
// These scripts are always executed as configured no mather who runs them.
ScriptSecurity struct {
*Security
runAs uint64
allow []uint64
deny []uint64
}
Script struct {
Name string `json:"name"`
Label string `json:"label"`
Description string `json:"description"`
Errors []string `json:"errors,omitempty"`
Triggers []*Trigger `json:"triggers"`
Name string `json:"name"`
Label string `json:"label"`
Description string `json:"description"`
Errors []string `json:"errors,omitempty"`
Triggers []*Trigger `json:"triggers"`
Security *ScriptSecurity `json:"security"`
// If bundle or type is set, consider
// this a frontend script
@@ -14,3 +35,28 @@ type (
Type string `json:"type,omitempty"`
}
)
// FindByName returns script from the set if it exists
func (set ScriptSet) FindByName(name string) *Script {
for i := range set {
if set[i].Name == name {
return set[i]
}
}
return nil
}
// String fn to make logging a bit friendlier
func (ss *ScriptSecurity) String() (o string) {
if ss == nil {
return "<nil>"
}
return fmt.Sprintf(
"runAs: %s, allow: %s, deny: %s",
ss.RunAs,
strings.Join(ss.Allow, ","),
strings.Join(ss.Deny, ","),
)
}
+6 -5
View File
@@ -1,6 +1,7 @@
package corredor
import (
"context"
"encoding/json"
"fmt"
"github.com/pkg/errors"
@@ -35,9 +36,9 @@ func popOnManualEventType(trigger *Trigger) (found bool) {
// pluckManualTriggers removes all manual triggers from the list of script's triggers
//
// and returns a hash map with resources from these manual triggers
func pluckManualTriggers(script *ServerScript) map[string]string {
func pluckManualTriggers(script *ServerScript) map[string]bool {
var (
hash = make(map[string]string)
hash = make(map[string]bool)
)
for i := range script.Triggers {
@@ -47,7 +48,7 @@ func pluckManualTriggers(script *ServerScript) map[string]string {
if popOnManualEventType(trigger) {
for _, res := range trigger.ResourceTypes {
hash[res] = trigger.RunAs
hash[res] = true
}
}
}
@@ -134,9 +135,9 @@ func makeScriptFilter(f Filter) func(s *Script) (b bool, err error) {
}
// GenericListHandler returns filtered list of scripts
func GenericListHandler(svc *service, f Filter, resourcePrefix string) (p *automationListSetPayload, err error) {
func GenericListHandler(ctx context.Context, svc *service, f Filter, resourcePrefix string) (p *automationListSetPayload, err error) {
f.PrefixResources(resourcePrefix)
p = &automationListSetPayload{}
p.Set, p.Filter, err = svc.Find(f)
p.Set, p.Filter, err = svc.Find(ctx, f)
return p, err
}
+4 -5
View File
@@ -31,16 +31,15 @@ func TestPluckManualTriggers(t *testing.T) {
Triggers: []*Trigger{&Trigger{
ResourceTypes: []string{"r1", "r2"},
EventTypes: []string{"onTimestamp", onManualEventType, "onInterval"},
RunAs: "moi",
}},
}
)
a.Len(s.Triggers[0].EventTypes, 3)
a.EqualValues(
map[string]string{
"r1": "moi",
"r2": "moi",
map[string]bool{
"r1": true,
"r2": true,
},
pluckManualTriggers(s),
)
@@ -48,7 +47,7 @@ func TestPluckManualTriggers(t *testing.T) {
// Running again must result in empty hash
a.EqualValues(
map[string]string{},
map[string]bool{},
pluckManualTriggers(s),
)
}
+1 -1
View File
@@ -43,8 +43,8 @@ func (app *App) Setup(log *zap.Logger, opts *app.Options) (err error) {
event.SystemOnTimestamp(),
)
// Wire in cross-service JWT maker for Corredor
corredor.Service().SetUserFinder(service.DefaultUser)
corredor.Service().SetRoleFinder(service.DefaultRole)
return
}
+1
View File
@@ -23,6 +23,7 @@ func (Automation) New() *Automation {
func (ctrl *Automation) List(ctx context.Context, r *request.AutomationList) (interface{}, error) {
return corredor.GenericListHandler(
ctx,
corredor.Service(),
corredor.Filter{
ResourceTypes: r.ResourceTypes,
+26
View File
@@ -2,6 +2,7 @@ package service
import (
"context"
"strconv"
"github.com/pkg/errors"
"github.com/titpetric/factory"
@@ -54,6 +55,7 @@ type (
FindByID(roleID uint64) (*types.Role, error)
FindByName(name string) (*types.Role, error)
FindByHandle(handle string) (*types.Role, error)
FindByAny(identifier interface{}) (*types.Role, error)
Find(types.RoleFilter) (types.RoleSet, types.RoleFilter, error)
Create(role *types.Role) (*types.Role, error)
@@ -146,6 +148,30 @@ func (svc role) FindByHandle(handle string) (*types.Role, error) {
return svc.role.FindByHandle(handle)
}
// FindByAny finds role by given identifier (id, handle, name)
func (svc role) FindByAny(identifier interface{}) (r *types.Role, err error) {
if ID, ok := identifier.(uint64); ok {
r, err = svc.FindByID(ID)
} else if strIdentifier, ok := identifier.(string); ok {
if ID, _ := strconv.ParseUint(strIdentifier, 10, 64); ID > 0 {
r, err = svc.FindByID(ID)
} else {
r, err = svc.FindByHandle(strIdentifier)
if err == nil && r.ID == 0 {
r, err = svc.FindByName(strIdentifier)
}
}
} else {
err = ErrInvalidID
}
if err != nil {
return
}
return
}
func (svc role) Create(new *types.Role) (r *types.Role, err error) {
if !handle.IsValid(new.Handle) {