github.com/annwntech/go-micro/v2@v2.9.5/auth/service/service.go (about)

     1  package service
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/annwntech/go-micro/v2/auth"
     9  	"github.com/annwntech/go-micro/v2/auth/rules"
    10  	pb "github.com/annwntech/go-micro/v2/auth/service/proto"
    11  	"github.com/annwntech/go-micro/v2/auth/token"
    12  	"github.com/annwntech/go-micro/v2/auth/token/jwt"
    13  	"github.com/annwntech/go-micro/v2/client"
    14  )
    15  
    16  // svc is the service implementation of the Auth interface
    17  type svc struct {
    18  	options auth.Options
    19  	auth    pb.AuthService
    20  	rules   pb.RulesService
    21  	jwt     token.Provider
    22  }
    23  
    24  func (s *svc) String() string {
    25  	return "service"
    26  }
    27  
    28  func (s *svc) Init(opts ...auth.Option) {
    29  	for _, o := range opts {
    30  		o(&s.options)
    31  	}
    32  
    33  	if s.options.Client == nil {
    34  		s.options.Client = client.DefaultClient
    35  	}
    36  
    37  	s.auth = pb.NewAuthService("go.micro.auth", s.options.Client)
    38  	s.rules = pb.NewRulesService("go.micro.auth", s.options.Client)
    39  
    40  	// if we have a JWT public key passed as an option,
    41  	// we can decode tokens with the type "JWT" locally
    42  	// and not have to make an RPC call
    43  	if key := s.options.PublicKey; len(key) > 0 {
    44  		s.jwt = jwt.NewTokenProvider(token.WithPublicKey(key))
    45  	}
    46  }
    47  
    48  func (s *svc) Options() auth.Options {
    49  	return s.options
    50  }
    51  
    52  // Generate a new account
    53  func (s *svc) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
    54  	options := auth.NewGenerateOptions(opts...)
    55  
    56  	rsp, err := s.auth.Generate(context.TODO(), &pb.GenerateRequest{
    57  		Id:       id,
    58  		Type:     options.Type,
    59  		Secret:   options.Secret,
    60  		Scopes:   options.Scopes,
    61  		Metadata: options.Metadata,
    62  		Provider: options.Provider,
    63  	})
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	return serializeAccount(rsp.Account), nil
    69  }
    70  
    71  // Grant access to a resource
    72  func (s *svc) Grant(rule *auth.Rule) error {
    73  	access := pb.Access_UNKNOWN
    74  	if rule.Access == auth.AccessGranted {
    75  		access = pb.Access_GRANTED
    76  	} else if rule.Access == auth.AccessDenied {
    77  		access = pb.Access_DENIED
    78  	}
    79  
    80  	_, err := s.rules.Create(context.TODO(), &pb.CreateRequest{
    81  		Rule: &pb.Rule{
    82  			Id:       rule.ID,
    83  			Scope:    rule.Scope,
    84  			Priority: rule.Priority,
    85  			Access:   access,
    86  			Resource: &pb.Resource{
    87  				Type:     rule.Resource.Type,
    88  				Name:     rule.Resource.Name,
    89  				Endpoint: rule.Resource.Endpoint,
    90  			},
    91  		},
    92  	})
    93  
    94  	return err
    95  }
    96  
    97  // Revoke access to a resource
    98  func (s *svc) Revoke(rule *auth.Rule) error {
    99  	_, err := s.rules.Delete(context.TODO(), &pb.DeleteRequest{
   100  		Id: rule.ID,
   101  	})
   102  
   103  	return err
   104  }
   105  
   106  func (s *svc) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
   107  	var options auth.RulesOptions
   108  	for _, o := range opts {
   109  		o(&options)
   110  	}
   111  	if options.Context == nil {
   112  		options.Context = context.TODO()
   113  	}
   114  
   115  	rsp, err := s.rules.List(options.Context, &pb.ListRequest{}, client.WithCache(time.Second*30))
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	rules := make([]*auth.Rule, len(rsp.Rules))
   121  	for i, r := range rsp.Rules {
   122  		rules[i] = serializeRule(r)
   123  	}
   124  
   125  	return rules, nil
   126  }
   127  
   128  // Verify an account has access to a resource
   129  func (s *svc) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
   130  	var options auth.VerifyOptions
   131  	for _, o := range opts {
   132  		o(&options)
   133  	}
   134  
   135  	rs, err := s.Rules(auth.RulesContext(options.Context))
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	return rules.VerifyAccess(rs, acc, res)
   141  }
   142  
   143  // Inspect a token
   144  func (s *svc) Inspect(token string) (*auth.Account, error) {
   145  	// try to decode JWT locally and fall back to srv if an error occurs
   146  	if len(strings.Split(token, ".")) == 3 && s.jwt != nil {
   147  		return s.jwt.Inspect(token)
   148  	}
   149  
   150  	// the token is not a JWT or we do not have the keys to decode it,
   151  	// fall back to the auth service
   152  	rsp, err := s.auth.Inspect(context.TODO(), &pb.InspectRequest{Token: token})
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	return serializeAccount(rsp.Account), nil
   157  }
   158  
   159  // Token generation using an account ID and secret
   160  func (s *svc) Token(opts ...auth.TokenOption) (*auth.AuthToken, error) {
   161  	options := auth.NewTokenOptions(opts...)
   162  
   163  	rsp, err := s.auth.Token(context.Background(), &pb.TokenRequest{
   164  		Id:           options.ID,
   165  		Secret:       options.Secret,
   166  		RefreshToken: options.RefreshToken,
   167  		TokenExpiry:  int64(options.Expiry.Seconds()),
   168  	})
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	return serializeToken(rsp.Token), nil
   174  }
   175  
   176  func serializeToken(t *pb.Token) *auth.AuthToken {
   177  	return &auth.AuthToken{
   178  		AccessToken:  t.AccessToken,
   179  		RefreshToken: t.RefreshToken,
   180  		Created:      time.Unix(t.Created, 0),
   181  		Expiry:       time.Unix(t.Expiry, 0),
   182  	}
   183  }
   184  
   185  func serializeAccount(a *pb.Account) *auth.Account {
   186  	return &auth.Account{
   187  		ID:       a.Id,
   188  		Secret:   a.Secret,
   189  		Issuer:   a.Issuer,
   190  		Metadata: a.Metadata,
   191  		Scopes:   a.Scopes,
   192  	}
   193  }
   194  
   195  func serializeRule(r *pb.Rule) *auth.Rule {
   196  	var access auth.Access
   197  	if r.Access == pb.Access_GRANTED {
   198  		access = auth.AccessGranted
   199  	} else {
   200  		access = auth.AccessDenied
   201  	}
   202  
   203  	return &auth.Rule{
   204  		ID:       r.Id,
   205  		Scope:    r.Scope,
   206  		Access:   access,
   207  		Priority: r.Priority,
   208  		Resource: &auth.Resource{
   209  			Type:     r.Resource.Type,
   210  			Name:     r.Resource.Name,
   211  			Endpoint: r.Resource.Endpoint,
   212  		},
   213  	}
   214  }
   215  
   216  // NewAuth returns a new instance of the Auth service
   217  func NewAuth(opts ...auth.Option) auth.Auth {
   218  	options := auth.NewOptions(opts...)
   219  	if options.Client == nil {
   220  		options.Client = client.DefaultClient
   221  	}
   222  
   223  	return &svc{
   224  		auth:    pb.NewAuthService("go.micro.auth", options.Client),
   225  		rules:   pb.NewRulesService("go.micro.auth", options.Client),
   226  		options: options,
   227  	}
   228  }