Skip to content
Snippets Groups Projects
Commit 90e79f39 authored by Alena Petraki's avatar Alena Petraki
Browse files

Изменена сигнатура go-клиентов Account, Content: grpc-соединение...

Изменена сигнатура go-клиентов Account, Content: grpc-соединение инициализируется заранее и передается внутрь конструктора
parent b68d9f33
No related branches found
No related tags found
No related merge requests found
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
}
......@@ -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
clientOptions []kitgrpc.ClientOption // todo: можно заменить на grpc-интерсепторы при соединении и избавиться здесь от go-kit
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
}
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
......
......@@ -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
}
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 {
......
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"`
}
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
}
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{
......
......@@ -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...))
}
......
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...)
}
}
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
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment