diff --git a/compose/app.go b/compose/app.go index 832495009..46b8b28b6 100644 --- a/compose/app.go +++ b/compose/app.go @@ -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 } diff --git a/compose/rest/automation.go b/compose/rest/automation.go index f6dc39233..f11557624 100644 --- a/compose/rest/automation.go +++ b/compose/rest/automation.go @@ -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, diff --git a/messaging/app.go b/messaging/app.go index b96b11c0c..2826dd4d7 100644 --- a/messaging/app.go +++ b/messaging/app.go @@ -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 } diff --git a/monolith/app.go b/monolith/app.go index 641239d13..1e1c3dd95 100644 --- a/monolith/app.go +++ b/monolith/app.go @@ -71,6 +71,7 @@ func (monolith App) Initialize(ctx context.Context) (err error) { } corredor.Service().SetUserFinder(systemService.DefaultUser) + corredor.Service().SetRoleFinder(systemService.DefaultRole) return } diff --git a/pkg/corredor/filter.go b/pkg/corredor/filter.go index d3bc4f524..7b6b8bfb3 100644 --- a/pkg/corredor/filter.go +++ b/pkg/corredor/filter.go @@ -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"` diff --git a/pkg/corredor/service-corredor.pb.go b/pkg/corredor/service-corredor.pb.go index 9054b4c98..ed9888937 100644 --- a/pkg/corredor/service-corredor.pb.go +++ b/pkg/corredor/service-corredor.pb.go @@ -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. diff --git a/pkg/corredor/service.go b/pkg/corredor/service.go index af1c01d29..7bf5b4e30 100644 --- a/pkg/corredor/service.go +++ b/pkg/corredor/service.go @@ -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[][] = - manual map[string]map[string]string + // map[][] = 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 diff --git a/pkg/corredor/service_test.go b/pkg/corredor/service_test.go index e43ea4227..f3841d740 100644 --- a/pkg/corredor/service_test.go +++ b/pkg/corredor/service_test.go @@ -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{}, } ) diff --git a/pkg/corredor/types.go b/pkg/corredor/types.go index 4a158207f..c41a0316e 100644 --- a/pkg/corredor/types.go +++ b/pkg/corredor/types.go @@ -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 "" + } + + return fmt.Sprintf( + "runAs: %s, allow: %s, deny: %s", + ss.RunAs, + strings.Join(ss.Allow, ","), + strings.Join(ss.Deny, ","), + ) +} diff --git a/pkg/corredor/util.go b/pkg/corredor/util.go index 62c958f21..c31aaa7ae 100644 --- a/pkg/corredor/util.go +++ b/pkg/corredor/util.go @@ -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 } diff --git a/pkg/corredor/util_test.go b/pkg/corredor/util_test.go index e7afb8f86..bf06f5e6e 100644 --- a/pkg/corredor/util_test.go +++ b/pkg/corredor/util_test.go @@ -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), ) } diff --git a/system/app.go b/system/app.go index 888172a25..fae391c06 100644 --- a/system/app.go +++ b/system/app.go @@ -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 } diff --git a/system/rest/automation.go b/system/rest/automation.go index 4773e1bb1..d8e499118 100644 --- a/system/rest/automation.go +++ b/system/rest/automation.go @@ -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, diff --git a/system/service/role.go b/system/service/role.go index c46f2aa2c..3d0d1eb08 100644 --- a/system/service/role.go +++ b/system/service/role.go @@ -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) {