3
0

Support run-as for automation scripts

Add grpc-server capabilities for system service (make jwt, find user by id)
Add jwt generation (via grpc) to compose for run-as automation-scripts
Add SuperUser for system-level tasks and operations that are ran in the background w/o initiator
and require permision checking
This commit is contained in:
Denis Arh
2019-08-22 19:12:24 +02:00
parent 1611f9ede7
commit 5bfafd4adc
21 changed files with 860 additions and 35 deletions

View File

@@ -170,6 +170,12 @@ function proto {
module.proto \
record.proto \
script_runner.proto
yellow " ${CORTEZA_PROTOBUF_PATH} >> system/proto"
PATH=$PATH:$GOPATH/bin protoc \
--proto_path ${CORTEZA_PROTOBUF_PATH}/system \
--go_out=plugins=grpc:system/proto \
user.proto
green "OK"
}

View File

@@ -36,8 +36,9 @@ func Configure() *cli.Config {
servicesInitialized = true
cli.HandleError(service.Init(ctx, c.Log, service.Config{
Storage: *c.StorageOpt,
ScriptRunner: *c.ScriptRunner,
Storage: *c.StorageOpt,
ScriptRunner: *c.ScriptRunner,
GRPCClientSystem: *c.GRPCServerSystem,
}))
},

View File

@@ -231,7 +231,7 @@ func (svc automationRunner) makeRecordScriptRunner(ctx context.Context, ns *type
defer cancelFn()
// Add invoker's or defined credentials/jwt
req.JWT = svc.getJWT(ctx, script.RunAs)
req.JWT = svc.getJWT(ctx, script)
// Add script info
req.Script = proto.FromAutomationScript(script)
@@ -295,10 +295,9 @@ func (svc automationRunner) makeRecordScriptRunner(ctx context.Context, ns *type
}
}
// Who modified/created/owns the Record
var currentUserID = auth.GetIdentityFromContext(ctx).Identity()
if script.RunAs > 0 {
currentUserID = script.RunAs
}
currentUserID = script.RunAs
if r.OwnedBy == 0 {
r.OwnedBy = currentUserID
@@ -317,11 +316,12 @@ func (svc automationRunner) makeRecordScriptRunner(ctx context.Context, ns *type
}
// Creates a new JWT for
func (svc automationRunner) getJWT(ctx context.Context, userID uint64) string {
if userID > 0 {
func (svc automationRunner) getJWT(ctx context.Context, script *automation.Script) string {
if script.RunAsDefined() {
// @todo implement this
// at the moment we do not he the ability fetch user info from non-system service
// extend/implement this feature when our services will know how to communicate with each-other
return script.Credentials()
}
return svc.jwtEncoder.Encode(auth.GetIdentityFromContext(ctx))

View File

@@ -8,10 +8,12 @@ import (
"github.com/cortezaproject/corteza-server/compose/internal/repository"
"github.com/cortezaproject/corteza-server/compose/proto"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/internal/permissions"
"github.com/cortezaproject/corteza-server/internal/store"
"github.com/cortezaproject/corteza-server/pkg/automation"
"github.com/cortezaproject/corteza-server/pkg/cli/options"
proto2 "github.com/cortezaproject/corteza-server/system/proto"
)
type (
@@ -20,16 +22,10 @@ type (
Watch(ctx context.Context)
}
// automationManager interface {
// automationScriptsFinder
// automationScriptManager
// automationTriggerManager
// Watch(ctx context.Context)
// }
Config struct {
Storage options.StorageOpt
ScriptRunner options.ScriptRunnerOpt
Storage options.StorageOpt
ScriptRunner options.ScriptRunnerOpt
GRPCClientSystem options.GRPCServerOpt
}
)
@@ -59,6 +55,8 @@ var (
DefaultAttachment AttachmentService
DefaultNotification NotificationService
DefaultSystemUser *systemUser
)
func Init(ctx context.Context, log *zap.Logger, c Config) (err error) {
@@ -80,12 +78,25 @@ func Init(ctx context.Context, log *zap.Logger, c Config) (err error) {
DefaultAccessControl = AccessControl(DefaultPermissions)
// ias Internal Automatinon Service
{
systemClientConn, err := NewSystemGRPCClient(ctx, c.GRPCClientSystem, DefaultLogger)
if err != nil {
return err
}
DefaultSystemUser = SystemUser(proto2.NewUsersClient(systemClientConn))
}
// ias: Internal Automatinon Service
// handles script & trigger management & keeping runnables cripts in internal cache
ias := automation.Service(automation.AutomationServiceConfig{
Logger: DefaultLogger,
DbTablePrefix: "compose",
DB: db,
TokenMaker: func(ctx context.Context, userID uint64) (s string, e error) {
ctx = auth.SetSuperUserContext(ctx)
return DefaultSystemUser.MakeJWT(ctx, userID)
},
})
// Pass automation manager to

View File

@@ -0,0 +1,33 @@
package service
import (
"context"
"go.uber.org/zap"
"go.uber.org/zap/zapgrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"github.com/cortezaproject/corteza-server/pkg/cli/options"
)
// Connects to system gRPC server
func NewSystemGRPCClient(ctx context.Context, opt options.GRPCServerOpt, logger *zap.Logger) (c *grpc.ClientConn, err error) {
if opt.ClientLog {
// Send logs to zap
//
// waiting for https://github.com/uber-go/zap/pull/538
grpclog.SetLogger(zapgrpc.NewLogger(logger.Named("grpc-client-system")))
}
var dopts = []grpc.DialOption{
// @todo insecure?
grpc.WithInsecure(),
}
if opt.ClientMaxBackoffDelay > 0 {
dopts = append(dopts, grpc.WithBackoffMaxDelay(opt.ClientMaxBackoffDelay))
}
return grpc.DialContext(ctx, opt.Addr, dopts...)
}

View File

@@ -0,0 +1,54 @@
package service
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/system/proto"
"github.com/cortezaproject/corteza-server/system/types"
)
// gRPC client for
type (
systemUser struct {
client proto.UsersClient
}
)
func SystemUser(c proto.UsersClient) *systemUser {
return &systemUser{
client: c,
}
}
func (svc systemUser) MakeJWT(ctx context.Context, ID uint64) (string, error) {
ctx = metadata.NewOutgoingContext(ctx, metadata.MD{
"jwt": []string{auth.GetJwtFromContext(ctx)},
})
rsp, err := svc.client.MakeJWT(ctx, &proto.MakeJWTRequest{UserID: ID}, grpc.WaitForReady(true))
if err != nil {
return "", err
}
return rsp.JWT, nil
}
func (svc systemUser) FindByID(ctx context.Context, ID uint64) (*types.User, error) {
rsp, err := svc.client.FindByID(ctx, &proto.FindByIDRequest{UserID: ID})
if err != nil {
return nil, err
}
return &types.User{
ID: rsp.User.ID,
Email: rsp.User.Email,
Name: rsp.User.Name,
Handle: rsp.User.Handle,
Kind: types.UserKind(rsp.User.Kind),
}, nil
}

View File

@@ -313,6 +313,10 @@ func (ctrl AutomationScript) makePayload(ctx context.Context, s *automation.Scri
CanGrant: ctrl.ac.CanGrant(ctx),
CanUpdate: ctrl.ac.CanUpdateAutomationScript(ctx, s),
CanDelete: ctrl.ac.CanDeleteAutomationScript(ctx, s),
CanSetRunner: ctrl.ac.CanGrant(ctx),
CanSetAsCritical: true,
CanSetAsAsync: true,
}, nil
}

View File

@@ -7,6 +7,10 @@ type (
}
)
const (
superUserID uint64 = 10000000000000000
)
func NewIdentity(id uint64, rr ...uint64) *Identity {
return &Identity{
id: id,
@@ -25,3 +29,11 @@ func (i Identity) Roles() []uint64 {
func (i Identity) Valid() bool {
return i.id > 0
}
func NewSuperUserIdentity() *Identity {
return NewIdentity(superUserID)
}
func IsSuperUser(i Identifiable) bool {
return superUserID == i.Identity()
}

View File

@@ -5,21 +5,42 @@ import (
)
type (
ctxKey int
)
var (
identityCtxKey ctxKey
identityCtxKey struct{}
jwtCtxKey struct{}
)
func SetIdentityToContext(ctx context.Context, identity Identifiable) context.Context {
return context.WithValue(ctx, identityCtxKey, identity)
return context.WithValue(ctx, identityCtxKey{}, identity)
}
func GetIdentityFromContext(ctx context.Context) Identifiable {
if identity, ok := ctx.Value(identityCtxKey).(Identifiable); ok {
if identity, ok := ctx.Value(identityCtxKey{}).(Identifiable); ok {
return identity
} else {
return NewIdentity(0)
}
}
func SetJwtToContext(ctx context.Context, jwt string) context.Context {
return context.WithValue(ctx, jwtCtxKey{}, jwt)
}
func GetJwtFromContext(ctx context.Context) string {
if jwt, ok := ctx.Value(jwtCtxKey{}).(string); ok {
return jwt
} else {
return ""
}
}
// SetSuperUserContext stores system user as identity
// and accompanying JWT for it to the context
func SetSuperUserContext(ctx context.Context) context.Context {
su := NewSuperUserIdentity()
ctx = SetIdentityToContext(ctx, su)
ctx = SetJwtToContext(ctx, DefaultJwtHandler.Encode(su))
return ctx
}

View File

@@ -15,10 +15,16 @@ type (
Encode(identity Identifiable) string
}
TokenDecoder interface {
Decode(token string) (Identifiable, error)
}
TokenHandler interface {
Encode(identity Identifiable) string
Verifier() func(http.Handler) http.Handler
Authenticator() func(http.Handler) http.Handler
TokenEncoder
TokenDecoder
HttpVerifier() func(http.Handler) http.Handler
HttpAuthenticator() func(http.Handler) http.Handler
}
Signer interface {

View File

@@ -44,10 +44,45 @@ func JWT(secret string, expiry int64) (jwt *token, err error) {
}
// Verifies JWT and stores it into context
func (t *token) Verifier() func(http.Handler) http.Handler {
func (t *token) HttpVerifier() func(http.Handler) http.Handler {
return jwtauth.Verifier(t.tokenAuth)
}
func (t *token) Decode(ts string) (Identifiable, error) {
var (
decoded, err = t.tokenAuth.Decode(ts)
rr []uint64
userID uint64
)
if err != nil {
return nil, err
}
if err = decoded.Claims.Valid(); err != nil {
return nil, err
}
if c, ok := decoded.Claims.(jwt.MapClaims); ok {
userID, _ = strconv.ParseUint(c["userID"].(string), 10, 64)
if memberOf, ok := c["memberOf"].(string); ok {
for _, str := range strings.Split(memberOf, " ") {
if id, _ := strconv.ParseUint(str, 10, 64); id > 0 {
rr = append(rr, id)
}
}
}
}
if userID > 0 {
return NewIdentity(userID, rr...), nil
}
return nil, errors.New("invalid claims")
}
func (t *token) Encode(identity Identifiable) string {
claims := jwt.MapClaims{
"userID": strconv.FormatUint(identity.Identity(), 10),
@@ -67,8 +102,8 @@ func (t *token) Encode(identity Identifiable) string {
return jwt
}
// Authenticator converts JWT claims into Identity and stores it into context
func (t *token) Authenticator() func(http.Handler) http.Handler {
// HttpAuthenticator converts JWT claims into Identity and stores it into context
func (t *token) HttpAuthenticator() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
jwt, claims, err := jwtauth.FromContext(r.Context())
@@ -94,7 +129,7 @@ func (t *token) Authenticator() func(http.Handler) http.Handler {
}
}
r = r.WithContext(SetIdentityToContext(r.Context(), identity))
r = r.WithContext(SetJwtToContext(SetIdentityToContext(r.Context(), identity), jwt.Raw))
}
next.ServeHTTP(w, r)

View File

@@ -51,6 +51,8 @@ func Service(ctx context.Context, logger *zap.Logger, repository *repository) (s
// use Check() to test against permission rules and
// iterate over all fallback functions
//
// System user is always allowed to do everything
//
// When not explicitly allowed through rules or fallbacks, function will return FALSE.
func (svc service) Can(ctx context.Context, res Resource, op Operation, ff ...CheckAccessFunc) bool {
{
@@ -62,7 +64,13 @@ func (svc service) Can(ctx context.Context, res Resource, op Operation, ff ...Ch
}
}
var roles = auth.GetIdentityFromContext(ctx).Roles()
u := auth.GetIdentityFromContext(ctx)
if auth.IsSuperUser(u) {
return true
}
var roles = u.Roles()
// Checking rules
var v = svc.Check(res, op, roles...)
if v != Inherit {

View File

@@ -125,8 +125,8 @@ func (s Server) Serve(ctx context.Context) {
router.Group(func(r chi.Router) {
r.Use(
auth.DefaultJwtHandler.Verifier(),
auth.DefaultJwtHandler.Authenticator(),
auth.DefaultJwtHandler.HttpVerifier(),
auth.DefaultJwtHandler.HttpAuthenticator(),
)
for _, mountRoutes := range s.endpoints {

View File

@@ -58,6 +58,12 @@ type (
// How are we merging?
tms triggersMergeStrategy
// Script running credentials
//
// We'll store credentials for security-defined scripts
// here when (runnable) scripts are loaded
credentials string
}
ScriptFilter struct {
@@ -180,6 +186,10 @@ func (s Script) HasEvent(event string) bool {
return s.triggers.HasMatch(Trigger{Event: event})
}
func (s Script) Credentials() string {
return s.credentials
}
func MakeMatcherIDCondition(id uint64) TriggerConditionChecker {
// We'll be comparing strings, not uint64!
var s = strconv.FormatUint(id, 10)

View File

@@ -26,6 +26,9 @@ type (
// internal list of runnable scripts (and their accompanying triggers)
runnables ScriptSet
// turns user-id (rel_runner / runAs) into valid credentials (JWT)
makeToken TokenMaker
srepo *scriptRepository
trepo *triggerRepository
@@ -36,6 +39,8 @@ type (
FilterByTrigger(event, resource string, cc ...TriggerConditionChecker) ScriptSet
}
TokenMaker func(context.Context, uint64) (string, error)
WatcherService interface {
Watch(ctx context.Context)
}
@@ -43,6 +48,7 @@ type (
AutomationServiceConfig struct {
Logger *zap.Logger
DB *factory.DB
TokenMaker TokenMaker
DbTablePrefix string
}
)
@@ -62,6 +68,8 @@ func Service(c AutomationServiceConfig) (svc *service) {
c: c,
makeToken: c.TokenMaker,
srepo: ScriptRepository(c.DbTablePrefix),
trepo: TriggerRepository(c.DbTablePrefix),
@@ -141,6 +149,24 @@ func (svc *service) reload(ctx context.Context) {
return
}
_ = svc.runnables.Walk(func(script *Script) (err error) {
if script.RunAsDefined() {
script.credentials, err = svc.makeToken(ctx, script.RunAs)
if err != nil {
script.Enabled = false
svc.logger.Info(
"could not make token, disabling script",
zap.Uint64("runAs", script.RunAs),
zap.Error(err),
)
}
}
return nil
})
return tt.Walk(func(t *Trigger) error {
s := svc.runnables.FindByID(t.ScriptID)
if s != nil && t.IsValid() && s.CheckCompatibility(t) == nil {

View File

@@ -0,0 +1,29 @@
package options
import (
"time"
)
type (
GRPCServerOpt struct {
Network string `env:"GRPC_SERVER_NETWORK"`
Addr string `env:"GRPC_SERVER_ADDR"`
ClientMaxBackoffDelay time.Duration `env:"GRPC_CLIENT_BACKOFF_DELAY"`
ClientLog bool `env:"GRPC_CLIENT_LOG"`
}
)
func GRPCServer(pfix string) (o *GRPCServerOpt) {
o = &GRPCServerOpt{
Network: "tcp",
Addr: ":50051",
ClientMaxBackoffDelay: time.Minute,
ClientLog: false,
}
fill(o, pfix)
return
}

View File

@@ -52,6 +52,12 @@ type (
StorageOpt *options.StorageOpt
ScriptRunner *options.ScriptRunnerOpt
// Services will be calling each other so we need
// to keep the config opts spearated
GRPCServerSystem *options.GRPCServerOpt
// GRPCServerMessaging *options.GRPCServerOpt
// GRPCServerCompose *options.GRPCServerOpt
// DB Connection name, defaults to ServiceName
DatabaseName string
@@ -190,6 +196,9 @@ func (c *Config) Init() {
c.SentryOpt = options.Sentry(c.EnvPrefix)
c.StorageOpt = options.Storage(c.EnvPrefix)
c.ScriptRunner = options.ScriptRunner(c.EnvPrefix)
c.GRPCServerSystem = options.GRPCServer("system")
// c.GRPCServerCompose = options.GRPCServer("compose")
// c.GRPCServerMessagign = options.GRPCServer("messaging")
if c.RootCommandDBSetup == nil {
c.RootCommandDBSetup = Runners{func(ctx context.Context, cmd *cobra.Command, c *Config) (err error) {

53
system/grpc/server.go Normal file
View File

@@ -0,0 +1,53 @@
package grpc
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/system/internal/service"
"github.com/cortezaproject/corteza-server/system/proto"
)
// @todo when we extend gRPC-server capabilities to compose & messaging
// this needs to be refactored and generalized
func NewServer() *grpc.Server {
s := grpc.NewServer(
grpc.UnaryInterceptor(authCheck(auth.DefaultJwtHandler)),
)
proto.RegisterUsersServer(s, NewUserService(
service.DefaultUser,
auth.DefaultJwtHandler,
service.DefaultAccessControl,
))
return s
}
// Creates auth-checking interceptor function
//
// Interceptor expects a valid 'jwt' in meta-data
func authCheck(h auth.TokenDecoder) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
if meta, ok := metadata.FromIncomingContext(ctx); !ok {
return nil, status.Error(codes.Unauthenticated, "could not read metadata")
} else if len(meta["jwt"]) != 1 {
return nil, status.Error(codes.Unauthenticated, "metadata without jwt")
} else if identity, err := h.Decode(meta["jwt"][0]); err != nil {
return nil, status.Error(codes.Unauthenticated, "invalid jwt")
} else if identity == nil || !identity.Valid() {
return nil, status.Error(codes.Unauthenticated, "invalid identity")
} else {
// Append identity to context and procede
ctx = auth.SetIdentityToContext(ctx, identity)
}
// Serve the request
return handler(ctx, req)
}
}

View File

@@ -0,0 +1,75 @@
package grpc
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/system/internal/service"
"github.com/cortezaproject/corteza-server/system/proto"
"github.com/cortezaproject/corteza-server/system/types"
)
type (
userService struct {
ac userServiceAccessControl
svc service.UserService
jwt auth.TokenEncoder
}
userServiceAccessControl interface {
CanGrant(ctx context.Context) bool
}
)
func NewUserService(svc service.UserService, jwt auth.TokenEncoder, ac userServiceAccessControl) *userService {
return &userService{
ac: ac,
svc: svc,
jwt: jwt,
}
}
func (gs userService) MakeJWT(ctx context.Context, req *proto.MakeJWTRequest) (rsp *proto.MakeJWTResponse, err error) {
var (
u *types.User
)
if !gs.ac.CanGrant(ctx) {
return nil, status.Error(codes.PermissionDenied, "no permissions to issue jwt for other users")
}
if u, err = gs.svc.FindByID(req.UserID); err != nil {
return
}
rsp = &proto.MakeJWTResponse{
JWT: gs.jwt.Encode(u),
}
return
}
func (gs userService) FindByID(ctx context.Context, req *proto.FindByIDRequest) (rsp *proto.FindByIDResponse, err error) {
var (
u *types.User
)
if u, err = gs.svc.FindByID(req.UserID); err != nil {
return
}
rsp = &proto.FindByIDResponse{
User: &proto.User{
ID: u.ID,
Email: u.Email,
Handle: u.Handle,
Name: u.Name,
Kind: string(u.Kind),
},
}
return
}

399
system/proto/user.pb.go Normal file
View File

@@ -0,0 +1,399 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: user.proto
package proto
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type MakeJWTRequest struct {
UserID uint64 `protobuf:"varint,1,opt,name=userID,proto3" json:"userID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MakeJWTRequest) Reset() { *m = MakeJWTRequest{} }
func (m *MakeJWTRequest) String() string { return proto.CompactTextString(m) }
func (*MakeJWTRequest) ProtoMessage() {}
func (*MakeJWTRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_116e343673f7ffaf, []int{0}
}
func (m *MakeJWTRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MakeJWTRequest.Unmarshal(m, b)
}
func (m *MakeJWTRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MakeJWTRequest.Marshal(b, m, deterministic)
}
func (m *MakeJWTRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_MakeJWTRequest.Merge(m, src)
}
func (m *MakeJWTRequest) XXX_Size() int {
return xxx_messageInfo_MakeJWTRequest.Size(m)
}
func (m *MakeJWTRequest) XXX_DiscardUnknown() {
xxx_messageInfo_MakeJWTRequest.DiscardUnknown(m)
}
var xxx_messageInfo_MakeJWTRequest proto.InternalMessageInfo
func (m *MakeJWTRequest) GetUserID() uint64 {
if m != nil {
return m.UserID
}
return 0
}
type MakeJWTResponse struct {
JWT string `protobuf:"bytes,1,opt,name=JWT,proto3" json:"JWT,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MakeJWTResponse) Reset() { *m = MakeJWTResponse{} }
func (m *MakeJWTResponse) String() string { return proto.CompactTextString(m) }
func (*MakeJWTResponse) ProtoMessage() {}
func (*MakeJWTResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_116e343673f7ffaf, []int{1}
}
func (m *MakeJWTResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MakeJWTResponse.Unmarshal(m, b)
}
func (m *MakeJWTResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MakeJWTResponse.Marshal(b, m, deterministic)
}
func (m *MakeJWTResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MakeJWTResponse.Merge(m, src)
}
func (m *MakeJWTResponse) XXX_Size() int {
return xxx_messageInfo_MakeJWTResponse.Size(m)
}
func (m *MakeJWTResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MakeJWTResponse.DiscardUnknown(m)
}
var xxx_messageInfo_MakeJWTResponse proto.InternalMessageInfo
func (m *MakeJWTResponse) GetJWT() string {
if m != nil {
return m.JWT
}
return ""
}
type FindByIDRequest struct {
UserID uint64 `protobuf:"varint,1,opt,name=userID,proto3" json:"userID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FindByIDRequest) Reset() { *m = FindByIDRequest{} }
func (m *FindByIDRequest) String() string { return proto.CompactTextString(m) }
func (*FindByIDRequest) ProtoMessage() {}
func (*FindByIDRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_116e343673f7ffaf, []int{2}
}
func (m *FindByIDRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FindByIDRequest.Unmarshal(m, b)
}
func (m *FindByIDRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FindByIDRequest.Marshal(b, m, deterministic)
}
func (m *FindByIDRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_FindByIDRequest.Merge(m, src)
}
func (m *FindByIDRequest) XXX_Size() int {
return xxx_messageInfo_FindByIDRequest.Size(m)
}
func (m *FindByIDRequest) XXX_DiscardUnknown() {
xxx_messageInfo_FindByIDRequest.DiscardUnknown(m)
}
var xxx_messageInfo_FindByIDRequest proto.InternalMessageInfo
func (m *FindByIDRequest) GetUserID() uint64 {
if m != nil {
return m.UserID
}
return 0
}
type FindByIDResponse struct {
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FindByIDResponse) Reset() { *m = FindByIDResponse{} }
func (m *FindByIDResponse) String() string { return proto.CompactTextString(m) }
func (*FindByIDResponse) ProtoMessage() {}
func (*FindByIDResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_116e343673f7ffaf, []int{3}
}
func (m *FindByIDResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FindByIDResponse.Unmarshal(m, b)
}
func (m *FindByIDResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FindByIDResponse.Marshal(b, m, deterministic)
}
func (m *FindByIDResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_FindByIDResponse.Merge(m, src)
}
func (m *FindByIDResponse) XXX_Size() int {
return xxx_messageInfo_FindByIDResponse.Size(m)
}
func (m *FindByIDResponse) XXX_DiscardUnknown() {
xxx_messageInfo_FindByIDResponse.DiscardUnknown(m)
}
var xxx_messageInfo_FindByIDResponse proto.InternalMessageInfo
func (m *FindByIDResponse) GetUser() *User {
if m != nil {
return m.User
}
return nil
}
type User struct {
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
Handle string `protobuf:"bytes,3,opt,name=handle,proto3" json:"handle,omitempty"`
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"`
Kind string `protobuf:"bytes,5,opt,name=kind,proto3" json:"kind,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *User) Reset() { *m = User{} }
func (m *User) String() string { return proto.CompactTextString(m) }
func (*User) ProtoMessage() {}
func (*User) Descriptor() ([]byte, []int) {
return fileDescriptor_116e343673f7ffaf, []int{4}
}
func (m *User) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_User.Unmarshal(m, b)
}
func (m *User) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_User.Marshal(b, m, deterministic)
}
func (m *User) XXX_Merge(src proto.Message) {
xxx_messageInfo_User.Merge(m, src)
}
func (m *User) XXX_Size() int {
return xxx_messageInfo_User.Size(m)
}
func (m *User) XXX_DiscardUnknown() {
xxx_messageInfo_User.DiscardUnknown(m)
}
var xxx_messageInfo_User proto.InternalMessageInfo
func (m *User) GetID() uint64 {
if m != nil {
return m.ID
}
return 0
}
func (m *User) GetEmail() string {
if m != nil {
return m.Email
}
return ""
}
func (m *User) GetHandle() string {
if m != nil {
return m.Handle
}
return ""
}
func (m *User) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *User) GetKind() string {
if m != nil {
return m.Kind
}
return ""
}
func init() {
proto.RegisterType((*MakeJWTRequest)(nil), "system.MakeJWTRequest")
proto.RegisterType((*MakeJWTResponse)(nil), "system.MakeJWTResponse")
proto.RegisterType((*FindByIDRequest)(nil), "system.FindByIDRequest")
proto.RegisterType((*FindByIDResponse)(nil), "system.FindByIDResponse")
proto.RegisterType((*User)(nil), "system.User")
}
func init() { proto.RegisterFile("user.proto", fileDescriptor_116e343673f7ffaf) }
var fileDescriptor_116e343673f7ffaf = []byte{
// 269 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0xbf, 0x4b, 0xc3, 0x40,
0x14, 0xc7, 0x49, 0x7a, 0x49, 0xf5, 0x29, 0x6d, 0x79, 0x48, 0x3d, 0x3a, 0x95, 0xb8, 0xd4, 0x25,
0x43, 0x75, 0x12, 0x5c, 0x4a, 0x10, 0x52, 0x70, 0x09, 0x95, 0x82, 0x5b, 0x24, 0x0f, 0x0c, 0x4d,
0x2e, 0x35, 0x97, 0x0e, 0x5d, 0xfd, 0xcb, 0xe5, 0x5e, 0xae, 0x11, 0xcd, 0xd0, 0x29, 0xef, 0xfb,
0x7d, 0x9f, 0xbc, 0x5f, 0x07, 0x70, 0xd0, 0x54, 0x87, 0xfb, 0xba, 0x6a, 0x2a, 0xf4, 0xf5, 0x51,
0x37, 0x54, 0x06, 0x0b, 0x18, 0xbd, 0xa6, 0x3b, 0x5a, 0x6f, 0x37, 0x09, 0x7d, 0x1d, 0x48, 0x37,
0x38, 0x05, 0xdf, 0x70, 0x71, 0x24, 0x9d, 0xb9, 0xb3, 0x10, 0x89, 0x55, 0xc1, 0x1d, 0x8c, 0x3b,
0x52, 0xef, 0x2b, 0xa5, 0x09, 0x27, 0x30, 0x58, 0x6f, 0x37, 0xcc, 0x5d, 0x26, 0x26, 0x0c, 0xee,
0x61, 0xfc, 0x92, 0xab, 0x6c, 0x75, 0x8c, 0xa3, 0x73, 0xf5, 0x1e, 0x61, 0xf2, 0x8b, 0xda, 0x82,
0x73, 0x10, 0x26, 0xcb, 0xe4, 0xd5, 0xf2, 0x3a, 0x6c, 0x87, 0x0c, 0xdf, 0x34, 0xd5, 0x09, 0x67,
0x82, 0x02, 0x84, 0x51, 0x38, 0x02, 0xb7, 0xab, 0xe8, 0xc6, 0x11, 0xde, 0x80, 0x47, 0x65, 0x9a,
0x17, 0xd2, 0xe5, 0x61, 0x5a, 0x61, 0x7a, 0x7f, 0xa6, 0x2a, 0x2b, 0x48, 0x0e, 0xd8, 0xb6, 0x0a,
0x11, 0x84, 0x4a, 0x4b, 0x92, 0x82, 0x5d, 0x8e, 0x8d, 0xb7, 0xcb, 0x55, 0x26, 0xbd, 0xd6, 0x33,
0xf1, 0xf2, 0xdb, 0x01, 0xcf, 0xb4, 0xd3, 0xf8, 0x04, 0x43, 0xbb, 0x3d, 0x4e, 0x4f, 0x63, 0xfd,
0x3d, 0xdc, 0xec, 0xb6, 0xe7, 0xdb, 0xad, 0x9e, 0xe1, 0xe2, 0xb4, 0x29, 0x76, 0xd0, 0xbf, 0x33,
0xcd, 0x64, 0x3f, 0xd1, 0xfe, 0xbe, 0x1a, 0xbe, 0x7b, 0xfc, 0x66, 0x1f, 0x3e, 0x7f, 0x1e, 0x7e,
0x02, 0x00, 0x00, 0xff, 0xff, 0x04, 0x91, 0x4b, 0x16, 0xc8, 0x01, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// UsersClient is the client API for Users service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type UsersClient interface {
MakeJWT(ctx context.Context, in *MakeJWTRequest, opts ...grpc.CallOption) (*MakeJWTResponse, error)
FindByID(ctx context.Context, in *FindByIDRequest, opts ...grpc.CallOption) (*FindByIDResponse, error)
}
type usersClient struct {
cc *grpc.ClientConn
}
func NewUsersClient(cc *grpc.ClientConn) UsersClient {
return &usersClient{cc}
}
func (c *usersClient) MakeJWT(ctx context.Context, in *MakeJWTRequest, opts ...grpc.CallOption) (*MakeJWTResponse, error) {
out := new(MakeJWTResponse)
err := c.cc.Invoke(ctx, "/system.Users/MakeJWT", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *usersClient) FindByID(ctx context.Context, in *FindByIDRequest, opts ...grpc.CallOption) (*FindByIDResponse, error) {
out := new(FindByIDResponse)
err := c.cc.Invoke(ctx, "/system.Users/FindByID", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// UsersServer is the server API for Users service.
type UsersServer interface {
MakeJWT(context.Context, *MakeJWTRequest) (*MakeJWTResponse, error)
FindByID(context.Context, *FindByIDRequest) (*FindByIDResponse, error)
}
// UnimplementedUsersServer can be embedded to have forward compatible implementations.
type UnimplementedUsersServer struct {
}
func (*UnimplementedUsersServer) MakeJWT(ctx context.Context, req *MakeJWTRequest) (*MakeJWTResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method MakeJWT not implemented")
}
func (*UnimplementedUsersServer) FindByID(ctx context.Context, req *FindByIDRequest) (*FindByIDResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method FindByID not implemented")
}
func RegisterUsersServer(s *grpc.Server, srv UsersServer) {
s.RegisterService(&_Users_serviceDesc, srv)
}
func _Users_MakeJWT_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MakeJWTRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UsersServer).MakeJWT(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/system.Users/MakeJWT",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UsersServer).MakeJWT(ctx, req.(*MakeJWTRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Users_FindByID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(FindByIDRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UsersServer).FindByID(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/system.Users/FindByID",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UsersServer).FindByID(ctx, req.(*FindByIDRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Users_serviceDesc = grpc.ServiceDesc{
ServiceName: "system.Users",
HandlerType: (*UsersServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "MakeJWT",
Handler: _Users_MakeJWT_Handler,
},
{
MethodName: "FindByID",
Handler: _Users_FindByID_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "user.proto",
}

View File

@@ -2,14 +2,17 @@ package system
import (
"context"
"net"
_ "github.com/joho/godotenv/autoload"
"github.com/spf13/cobra"
"github.com/titpetric/factory"
"go.uber.org/zap"
"github.com/cortezaproject/corteza-server/pkg/cli"
"github.com/cortezaproject/corteza-server/system/commands"
migrate "github.com/cortezaproject/corteza-server/system/db"
"github.com/cortezaproject/corteza-server/system/grpc"
"github.com/cortezaproject/corteza-server/system/internal/auth/external"
"github.com/cortezaproject/corteza-server/system/internal/service"
"github.com/cortezaproject/corteza-server/system/rest"
@@ -69,6 +72,36 @@ func Configure() *cli.Config {
service.DefaultAuthSettings, _ = service.DefaultSettings.LoadAuthSettings()
}
{
var (
grpcLog = c.Log.Named("grpc-server")
grpcLogConn = grpcLog.With(zap.String("addr", c.GRPCServerSystem.Addr))
)
// Temporary gRPC server initialization location
grpcServer := grpc.NewServer()
ln, err := net.Listen(c.GRPCServerSystem.Network, c.GRPCServerSystem.Addr)
if err != nil {
grpcLogConn.Error("could not start gRPC server", zap.Error(err))
}
go func() {
select {
case <-ctx.Done():
grpcLogConn.Debug("shutting down")
grpcServer.GracefulStop()
_ = ln.Close()
}
}()
go func() {
grpcLogConn.Info("Starting gRPC server")
err := grpcServer.Serve(ln)
grpcLogConn.Info("stopped", zap.Error(err))
}()
}
// Initialize external authentication (from default settings)
external.Init()
go service.Watchers(ctx)