diff --git a/pkg/account/client.go b/pkg/account/client.go index b81274020d2c80d640490a3e62ffa6d61344aed3..dcc46e282142dcdb5530f9d9b3898ac5ed885daa 100644 --- a/pkg/account/client.go +++ b/pkg/account/client.go @@ -1,15 +1,9 @@ package account import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "net/url" "time" "git.perx.ru/perxis/perxis-go/pkg/cache" - "git.perx.ru/perxis/perxis-go/pkg/errors" serviceMembers "git.perx.ru/perxis/perxis-go/pkg/members/middleware" membersObserverTransport "git.perx.ru/perxis/perxis-go/pkg/members/observer/transport/grpc" membersTransport "git.perx.ru/perxis/perxis-go/pkg/members/transport/grpc" @@ -17,13 +11,8 @@ import ( organizationsTransport "git.perx.ru/perxis/perxis-go/pkg/organizations/transport/grpc" serviceUsers "git.perx.ru/perxis/perxis-go/pkg/users/middleware" usersTransport "git.perx.ru/perxis/perxis-go/pkg/users/transport/grpc" - "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "go.uber.org/zap" - "golang.org/x/oauth2/clientcredentials" "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/credentials/oauth" ) const ( @@ -31,7 +20,7 @@ const ( DefaultCacheTTL = time.Second * 10 ) -func NewClient(ctx context.Context, addr string, opts ...Option) (*Account, *grpc.ClientConn, error) { +func NewClient(conn *grpc.ClientConn, opts ...Option) (*Account, error) { client := &Account{} c := &config{} @@ -45,23 +34,10 @@ func NewClient(ctx context.Context, addr string, opts ...Option) (*Account, *grp c.logger = zap.NewNop() } - c.dialOptions = append(c.dialOptions, grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()), - grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor())) - - authOptions, err := getAuthDialOpts(ctx, c) - if err != nil { - return nil, nil, err - } - - accountConn, err := grpc.Dial(addr, append(c.dialOptions, authOptions...)...) - if err != nil { - return nil, nil, err - } - - client.Members = membersTransport.NewGRPCClient(accountConn, "", c.clientOptions...) - client.Organizations = organizationsTransport.NewGRPCClient(accountConn, "", c.clientOptions...) - client.Users = usersTransport.NewGRPCClient(accountConn, "", c.clientOptions...) - client.MembersObserver = membersObserverTransport.NewGRPCClient(accountConn, "", c.clientOptions...) + client.Members = membersTransport.NewGRPCClient(conn, "", c.clientOptions...) + client.Organizations = organizationsTransport.NewGRPCClient(conn, "", c.clientOptions...) + client.Users = usersTransport.NewGRPCClient(conn, "", c.clientOptions...) + client.MembersObserver = membersObserverTransport.NewGRPCClient(conn, "", c.clientOptions...) if !c.noCache { client = WithCaching(client, DefaultCacheSize, DefaultCacheTTL) @@ -71,7 +47,7 @@ func NewClient(ctx context.Context, addr string, opts ...Option) (*Account, *grp client = WithLogging(client, c.logger, c.accessLog) } - return client, accountConn, nil + return client, nil } func WithCaching(client *Account, size int, ttl time.Duration) *Account { @@ -93,62 +69,3 @@ func WithLogging(client *Account, logger *zap.Logger, accessLog bool) *Account { return &c } - -func getAuthDialOpts(ctx context.Context, c *config) (opts []grpc.DialOption, err error) { - - if c.auth == nil { - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) - return - } - - if c.auth.oauth2 != nil { - // create external grpc client - conf := &clientcredentials.Config{ - TokenURL: c.auth.oauth2.tokenURL, - ClientID: c.auth.oauth2.clientID, - ClientSecret: c.auth.oauth2.clientSecret, - EndpointParams: url.Values{"audience": {c.auth.oauth2.audience}}, - } - - // обязательно использовать tls для credentials oauth.TokenSource https://github.com/grpc/grpc-go/blob/64031cbfcf4d84c026be93ad7b74b3c290100893/credentials/oauth/oauth.go#L160 - if c.auth.insecure { - return nil, errors.New("oauth requires tls") - } - if c.auth.tls == nil { - c.auth.tls = &tlsConfig{skipVerify: true} - } - - opts = append(opts, grpc.WithPerRPCCredentials(oauth.TokenSource{TokenSource: conf.TokenSource(ctx)})) - } - - if c.auth.tls != nil { - - if c.auth.tls.skipVerify { - opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}))) - - } else { - certPool := x509.NewCertPool() - if !certPool.AppendCertsFromPEM(c.auth.tls.cacert) { - return nil, fmt.Errorf("CA certificate not loaded") - } - - clientCert, err := tls.X509KeyPair(c.auth.tls.cert, c.auth.tls.key) - if err != nil { - return nil, err - } - - opts = append(opts, - grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ - Certificates: []tls.Certificate{clientCert}, - RootCAs: certPool, - })), - ) - } - } - - if c.auth.insecure { - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) - } - - return -} diff --git a/pkg/account/config.go b/pkg/account/config.go index fa2133602f940aa467fca1f44be6baa1f0ca966b..cfa3f088e56829ad3917e274f6e59938fa0098d7 100644 --- a/pkg/account/config.go +++ b/pkg/account/config.go @@ -3,91 +3,19 @@ package account import ( kitgrpc "github.com/go-kit/kit/transport/grpc" "go.uber.org/zap" - "google.golang.org/grpc" ) type config struct { - auth *authConfig - noCache bool - noLog bool - accessLog bool - debug bool - - clientOptions []kitgrpc.ClientOption - dialOptions []grpc.DialOption - - logger *zap.Logger -} - -type authConfig struct { - oauth2 *oauth2Config - tls *tlsConfig - insecure bool -} - -type oauth2Config struct { - tokenURL string - clientID string // параметр из auth0 (клиент с таким id должен быть создан в perxis) - clientSecret string - audience string // параметр из auth0 (название связанного с Application API) -} - -type tlsConfig struct { - cacert []byte - cert []byte - key []byte - skipVerify bool + noCache bool + noLog bool + accessLog bool + debug bool + clientOptions []kitgrpc.ClientOption // todo: можно заменить на grpc-интерсепторы при соединении и избавиться здесь от go-kit + logger *zap.Logger } type Option func(c *config) -func AuthOAuth2(tokenUrl, clientID, clientSecret, audience string) Option { - return func(c *config) { - if c.auth == nil { - c.auth = &authConfig{} - } - c.auth.oauth2 = &oauth2Config{ - tokenURL: tokenUrl, - clientID: clientID, - clientSecret: clientSecret, - audience: audience, - } - } -} - -func AuthTLS(cacert, cert, key []byte) Option { - return func(c *config) { - if c.auth == nil { - c.auth = &authConfig{} - } - c.auth.tls = &tlsConfig{ - cacert: cacert, - cert: cert, - key: key, - } - } -} - -func AuthInsecure() Option { - return func(c *config) { - if c.auth == nil { - c.auth = &authConfig{} - } - c.auth.insecure = true - } -} - -func AuthTLSSkipVerify() Option { - return func(c *config) { - if c.auth == nil { - c.auth = &authConfig{} - } - c.auth.tls = &tlsConfig{ - skipVerify: true, - } - } -} - func NoCache() Option { return func(c *config) { c.noCache = true @@ -106,12 +34,6 @@ func GrpcClientOptions(opts ...kitgrpc.ClientOption) Option { } } -func GrpcDialOptionsOptions(opts ...grpc.DialOption) Option { - return func(c *config) { - c.dialOptions = opts - } -} - func Logger(logger *zap.Logger) Option { return func(c *config) { c.logger = logger diff --git a/pkg/auth/grpc.go b/pkg/auth/grpc.go index 7a566711db76c11b22206d947ba94ecc2bc3b366..92399d0ccfbe1540d2db32ee1345662960f97eaf 100644 --- a/pkg/auth/grpc.go +++ b/pkg/auth/grpc.go @@ -2,9 +2,16 @@ package auth import ( "context" + "crypto/tls" + "crypto/x509" + "net/url" + "git.perx.ru/perxis/perxis-go/pkg/errors" kitgrpc "github.com/go-kit/kit/transport/grpc" + "golang.org/x/oauth2/clientcredentials" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/oauth" "google.golang.org/grpc/metadata" ) @@ -90,3 +97,35 @@ func PrincipalClientInterceptor() grpc.UnaryClientInterceptor { return invoker(ctx, method, req, reply, cc, opts...) } } + +func AddAuthorizationInterceptor(auth string) grpc.UnaryClientInterceptor { + return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + ctx = metadata.AppendToOutgoingContext(ctx, "authorization", auth) + return invoker(ctx, method, req, reply, cc, opts...) + } +} + +// WithOAuth2Credentials возвращает опции для создания grpc-соединения с аутентификацией oauth2. Переданный контекст +// будет использован для каждого запроса токена, поэтому он не может быть с таймаутом. +func WithOAuth2Credentials(ctx context.Context, tokenURL, clientID, clientSecret, audience string) grpc.DialOption { + conf := &clientcredentials.Config{ + TokenURL: tokenURL, + ClientID: clientID, + ClientSecret: clientSecret, + EndpointParams: url.Values{"audience": {audience}}, + } + return grpc.WithPerRPCCredentials(oauth.TokenSource{TokenSource: conf.TokenSource(ctx)}) +} + +// WithTLSCredentials возвращает опции для создания grpc-соединения с TLS-сертификатами +func WithTLSCredentials(ctx context.Context, cert, cacert, key []byte) (grpc.DialOption, error) { + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(cacert) { + return nil, errors.New("CA certificate not loaded") + } + clientCert, err := tls.X509KeyPair(cert, key) + if err != nil { + return nil, err + } + return grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{Certificates: []tls.Certificate{clientCert}, RootCAs: certPool})), nil +} diff --git a/pkg/content/client.go b/pkg/content/client.go index 8e642719e6bffbf2d6579f1f34f6a158a672ffc2..af5ba5bf9956cfaa51e0636acff0d98c82edfb38 100644 --- a/pkg/content/client.go +++ b/pkg/content/client.go @@ -1,7 +1,6 @@ package content import ( - "context" "time" "git.perx.ru/perxis/perxis-go/pkg/cache" @@ -34,8 +33,7 @@ const ( DefaultCacheTTL = time.Second * 10 ) -func NewClient(addr string, opts ...Option) (*Content, *grpc.ClientConn, error) { - ctx := context.Background() +func NewClient(conn *grpc.ClientConn, opts ...Option) *Content { client := &Content{} config := &Config{} @@ -48,26 +46,16 @@ func NewClient(addr string, opts ...Option) (*Content, *grpc.ClientConn, error) config.Logger = zap.NewNop() } - authDialOpts, err := config.GetAuthDialOpts(ctx) - if err != nil { - return nil, nil, err - } - - contentConn, err := grpc.Dial(addr, append(config.DialOptions, authDialOpts...)...) - if err != nil { - return nil, nil, err - } - - client.Spaces = spacesTransportGrpc.NewClient(contentConn, config.ClientOptions...) - client.Environments = environmentsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...) - client.Collections = collectionsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...) - client.Items = itemsTransportGrpc.NewClient(contentConn, config.ClientOptions...) - client.Invitations = invitationsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...) - client.Collaborators = collaboratorsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...) - client.Clients = clientsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...) - client.Locales = localsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...) - client.Roles = rolesTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...) - client.References = referencesTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...) + client.Spaces = spacesTransportGrpc.NewClient(conn, config.ClientOptions...) + client.Environments = environmentsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...) + client.Collections = collectionsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...) + client.Items = itemsTransportGrpc.NewClient(conn, config.ClientOptions...) + client.Invitations = invitationsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...) + client.Collaborators = collaboratorsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...) + client.Clients = clientsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...) + client.Locales = localsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...) + client.Roles = rolesTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...) + client.References = referencesTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...) if !config.NoDecode { client.Items = itemsSvc.ClientEncodeMiddleware(client.Collections)(client.Items) @@ -82,7 +70,7 @@ func NewClient(addr string, opts ...Option) (*Content, *grpc.ClientConn, error) client = WithLogging(client, config.Logger, config.AccessLog) } - return client, contentConn, nil + return client } func WithCaching(client *Content, size int, ttl time.Duration) *Content { diff --git a/pkg/content/config.go b/pkg/content/config.go index e4280505ab24310ee74063717081765ddd8d886e..37c6cc537edadcb29f06195963cc7d71d4f38c49 100644 --- a/pkg/content/config.go +++ b/pkg/content/config.go @@ -1,25 +1,11 @@ package content import ( - "context" - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "net/url" - - "git.perx.ru/perxis/perxis-go/pkg/transport" kitgrpc "github.com/go-kit/kit/transport/grpc" "go.uber.org/zap" - "golang.org/x/oauth2/clientcredentials" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/credentials/oauth" ) type Config struct { - Auth *AuthConfig NoCache bool NoLog bool AccessLog bool @@ -27,100 +13,11 @@ type Config struct { NoDecode bool ClientOptions []kitgrpc.ClientOption - DialOptions []grpc.DialOption - - Logger *zap.Logger -} - -type AuthConfig struct { - *OAuth2 `json:"oauth_2,omitempty"` - *APIKey `json:"api_key,omitempty"` - *TLS `json:"tls,omitempty"` - Insecure bool `json:"insecure,omitempty"` -} - -type OAuth2 struct { - TokenURL string `json:"token_url,omitempty"` - ClientID string `json:"client_id,omitempty"` // параметр из auth0 (клиент с таким id должен быть создан в perxis) - ClientSecret string `json:"client_secret,omitempty"` - Audience string `json:"audience,omitempty"` // параметр из auth0 (название связанного с Application API) -} - -type TLS struct { - CaCert []byte `json:"tls-cacert"` - Cert []byte `json:"tls-cert"` - Key []byte `json:"tls-key"` - SkipVerify bool -} - -type APIKey struct { - APIKey string `json:"api_key"` + Logger *zap.Logger } type Option func(c *Config) -func AuthOAuth2(tokenUrl, clientID, clientSecret, audience string) Option { - return func(c *Config) { - if c.Auth == nil { - c.Auth = &AuthConfig{} - } - c.Auth.OAuth2 = &OAuth2{ - TokenURL: tokenUrl, - ClientID: clientID, - ClientSecret: clientSecret, - Audience: audience, - } - } -} - -func AuthTLS(cacert, cert, key []byte) Option { - return func(c *Config) { - if c.Auth == nil { - c.Auth = &AuthConfig{} - } - c.Auth.TLS = &TLS{ - CaCert: cacert, - Cert: cert, - Key: key, - } - } -} - -func AuthTLSSkipVerify() Option { - return func(c *Config) { - if c.Auth == nil { - c.Auth = &AuthConfig{} - } - c.Auth.TLS = &TLS{ - SkipVerify: true, - } - } -} - -func AuthAPIKey(key string) Option { - return func(c *Config) { - if c.Auth == nil { - c.Auth = &AuthConfig{} - } - c.Auth.APIKey = &APIKey{APIKey: key} - } -} - -func AuthInsecure() Option { - return func(c *Config) { - if c.Auth == nil { - c.Auth = &AuthConfig{} - } - c.Auth.Insecure = true - } -} - -func Auth(cfg *AuthConfig) Option { - return func(c *Config) { - c.Auth = cfg - } -} - func NoCache() Option { return func(c *Config) { c.NoCache = true @@ -150,75 +47,3 @@ func GrpcClientOptions(opts ...kitgrpc.ClientOption) Option { c.ClientOptions = opts } } - -func GrpcDialOptionsOptions(opts ...grpc.DialOption) Option { - return func(c *Config) { - c.DialOptions = opts - } -} - -func (c *Config) GetAuthDialOpts(ctx context.Context) (opts []grpc.DialOption, err error) { - - if c.Auth == nil { - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) - return - } - - switch { - case c.Auth.OAuth2 != nil: - // create external grpc client - conf := &clientcredentials.Config{ - TokenURL: c.Auth.OAuth2.TokenURL, - ClientID: c.Auth.OAuth2.ClientID, - ClientSecret: c.Auth.OAuth2.ClientSecret, - EndpointParams: url.Values{"audience": {c.Auth.OAuth2.Audience}}, - } - - // обязательно использовать tls для credentials oauth.TokenSource https://github.com/grpc/grpc-go/blob/64031cbfcf4d84c026be93ad7b74b3c290100893/credentials/oauth/oauth.go#L160 - if c.Auth.Insecure { - return nil, errors.New("oauth requires tls") - } - if c.Auth.TLS == nil { - c.Auth.TLS = &TLS{SkipVerify: true} - } - - opts = append(opts, grpc.WithPerRPCCredentials(oauth.TokenSource{TokenSource: conf.TokenSource(ctx)})) - case c.Auth.APIKey != nil: - - if !c.Auth.Insecure && c.Auth.TLS == nil { - c.Auth.TLS = &TLS{SkipVerify: true} - } - opts = append(opts, grpc.WithUnaryInterceptor(transport.AddAuthorizationInterceptor("API-Key "+c.Auth.APIKey.APIKey))) - } - - if c.Auth.TLS != nil { - - if c.Auth.TLS.SkipVerify { - opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}))) - - } else { - certPool := x509.NewCertPool() - if !certPool.AppendCertsFromPEM(c.Auth.TLS.CaCert) { - return nil, fmt.Errorf("CA certificate not loaded") - } - - clientCert, err := tls.X509KeyPair(c.Auth.TLS.Cert, c.Auth.TLS.Key) - if err != nil { - return nil, err - } - - opts = append(opts, - grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ - Certificates: []tls.Certificate{clientCert}, - RootCAs: certPool, - })), - ) - } - } - - if c.Auth.Insecure { - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) - } - - return -} diff --git a/pkg/delivery/client/client.go b/pkg/delivery/client/client.go index 4f7d9a97ab4a978f3fd6c5708f6b5691067b64c0..1de0dd684a2344264ad217c2834fbb74f19d837f 100644 --- a/pkg/delivery/client/client.go +++ b/pkg/delivery/client/client.go @@ -1,7 +1,6 @@ package client import ( - "context" "time" "git.perx.ru/perxis/perxis-go/pkg/cache" @@ -15,26 +14,13 @@ import ( "google.golang.org/grpc" ) -func NewClient(addr string, opts ...Option) (delivery.Delivery, error) { - ctx := context.Background() +func NewClient(conn *grpc.ClientConn, opts ...Option) (delivery.Delivery, error) { c := new(Config) - dialOpts := make([]grpc.DialOption, 0) - for _, o := range opts { o(&c.Config) } - authDialOpts, err := c.GetAuthDialOpts(ctx) - if err != nil { - return nil, err - } - - conn, err := grpc.Dial(addr, append(dialOpts, authDialOpts...)...) - if err != nil { - return nil, err - } - client := deliverytransportgrpc.NewGRPCClient(conn, "", c.ClientOptions...) cfg := &deliveryservice.Config{ diff --git a/pkg/delivery/client/config.go b/pkg/delivery/client/config.go index 27f34b0bef42e7ad5aa40093a2f4188f62d1c556..ca1a65421a5a988a2171f32edd9e0abdf2ed0e2c 100644 --- a/pkg/delivery/client/config.go +++ b/pkg/delivery/client/config.go @@ -11,22 +11,6 @@ type Config struct { contentclient.Config } -func AuthOAuth2(tokenUrl, clientID, clientSecret, audience string) Option { - return Option(contentclient.AuthOAuth2(tokenUrl, clientID, clientSecret, audience)) -} - -func AuthTLS(cacert, cert, key []byte) Option { - return Option(contentclient.AuthTLS(cacert, cert, key)) -} - -func AuthAPIKey(key string) Option { - return Option(contentclient.AuthAPIKey(key)) -} - -func AuthInsecure() Option { - return Option(contentclient.AuthInsecure()) -} - func GrpcClientOptions(opts ...grpc.ClientOption) Option { return Option(contentclient.GrpcClientOptions(opts...)) } diff --git a/pkg/transport/grpc.go b/pkg/transport/grpc.go deleted file mode 100644 index adf54f5742b7f714f5cc3ef0970a2174883ab717..0000000000000000000000000000000000000000 --- a/pkg/transport/grpc.go +++ /dev/null @@ -1,15 +0,0 @@ -package transport - -import ( - "context" - - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" -) - -func AddAuthorizationInterceptor(auth string) grpc.UnaryClientInterceptor { - return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { - ctx = metadata.AppendToOutgoingContext(ctx, "authorization", auth) - return invoker(ctx, method, req, reply, cc, opts...) - } -} diff --git a/pkg/transport/http.go b/pkg/transport/http.go deleted file mode 100644 index 032c051591e0e54bc143c2f8dac10386e437c033..0000000000000000000000000000000000000000 --- a/pkg/transport/http.go +++ /dev/null @@ -1,35 +0,0 @@ -package transport - -import ( - "bytes" - "io" - "net/http" - "strings" - - "git.perx.ru/perxis/perxis-go/pkg/errors" - jsoniter "github.com/json-iterator/go" -) - -func DecodeError(r *http.Response) error { - if r.StatusCode >= 200 && r.StatusCode < 300 { - return nil - } - - var buf bytes.Buffer - if _, err := io.Copy(&buf, io.LimitReader(r.Body, 1024)); err != nil { - return err - } - - e := struct { - Error error `json:"error"` - }{} - if err := jsoniter.Unmarshal(buf.Bytes(), e); err != nil { - msg := strings.TrimSpace(buf.String()) - if msg == "" { - msg = http.StatusText(r.StatusCode) - } - return errors.New(msg) - } - - return e.Error -}