github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/auth/handler/rules.go (about)

     1  package handler
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"strings"
     7  	"sync"
     8  
     9  	pb "github.com/tickoalcantara12/micro/v3/proto/auth"
    10  	"github.com/tickoalcantara12/micro/v3/service/auth"
    11  	"github.com/tickoalcantara12/micro/v3/service/errors"
    12  	"github.com/tickoalcantara12/micro/v3/service/logger"
    13  	"github.com/tickoalcantara12/micro/v3/service/store"
    14  	"github.com/tickoalcantara12/micro/v3/util/auth/namespace"
    15  )
    16  
    17  const (
    18  	storePrefixRules = "rules"
    19  	joinKey          = "/"
    20  )
    21  
    22  var defaultRule = &auth.Rule{
    23  	ID:     "default",
    24  	Scope:  auth.ScopePublic,
    25  	Access: auth.AccessGranted,
    26  	Resource: &auth.Resource{
    27  		Type:     "*",
    28  		Name:     "*",
    29  		Endpoint: "*",
    30  	},
    31  }
    32  
    33  // Rules processes RPC calls
    34  type Rules struct {
    35  	Options auth.Options
    36  
    37  	namespaces map[string]bool
    38  	sync.Mutex
    39  }
    40  
    41  // Init the auth
    42  func (r *Rules) Init(opts ...auth.Option) {
    43  	for _, o := range opts {
    44  		o(&r.Options)
    45  	}
    46  }
    47  
    48  func (r *Rules) setupDefaultRules(ns string) {
    49  	r.Lock()
    50  	defer r.Unlock()
    51  
    52  	// setup the namespace cache if not yet done
    53  	if r.namespaces == nil {
    54  		r.namespaces = make(map[string]bool)
    55  	}
    56  
    57  	// check to see if the default rule has already been verified
    58  	if _, ok := r.namespaces[ns]; ok {
    59  		return
    60  	}
    61  
    62  	// check to see if we need to create the default account
    63  	key := strings.Join([]string{storePrefixRules, ns, ""}, joinKey)
    64  	recs, err := store.DefaultStore.Read(key, store.ReadPrefix())
    65  	if err != nil {
    66  		return
    67  	}
    68  
    69  	// create the account if none exist in the namespace
    70  	if len(recs) == 0 {
    71  		rule := &pb.Rule{
    72  			Id:     defaultRule.ID,
    73  			Scope:  defaultRule.Scope,
    74  			Access: pb.Access_GRANTED,
    75  			Resource: &pb.Resource{
    76  				Type:     defaultRule.Resource.Type,
    77  				Name:     defaultRule.Resource.Name,
    78  				Endpoint: defaultRule.Resource.Endpoint,
    79  			},
    80  		}
    81  
    82  		if err := r.writeRule(rule, ns); err != nil {
    83  			if logger.V(logger.WarnLevel, logger.DefaultLogger) {
    84  				logger.Warnf("Error creating default rule: %v", err)
    85  			}
    86  		}
    87  	}
    88  
    89  	// set the namespace in the cache
    90  	r.namespaces[ns] = true
    91  }
    92  
    93  // Create a rule giving a scope access to a resource
    94  func (r *Rules) Create(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error {
    95  	// Validate the request
    96  	if req.Rule == nil {
    97  		return errors.BadRequest("auth.Rules.Create", "Rule missing")
    98  	}
    99  	if len(req.Rule.Id) == 0 {
   100  		return errors.BadRequest("auth.Rules.Create", "ID missing")
   101  	}
   102  	if req.Rule.Resource == nil {
   103  		return errors.BadRequest("auth.Rules.Create", "Resource missing")
   104  	}
   105  	if req.Rule.Access == pb.Access_UNKNOWN {
   106  		return errors.BadRequest("auth.Rules.Create", "Access missing")
   107  	}
   108  
   109  	// set defaults
   110  	if req.Options == nil {
   111  		req.Options = &pb.Options{}
   112  	}
   113  	if len(req.Options.Namespace) == 0 {
   114  		req.Options.Namespace = namespace.DefaultNamespace
   115  	}
   116  
   117  	// authorize the request
   118  	if err := namespace.AuthorizeAdmin(ctx, req.Options.Namespace, "auth.Rules.Create"); err != nil {
   119  		return err
   120  	}
   121  
   122  	// write the rule to the store
   123  	return r.writeRule(req.Rule, req.Options.Namespace)
   124  }
   125  
   126  // Delete a scope access to a resource
   127  func (r *Rules) Delete(ctx context.Context, req *pb.DeleteRequest, rsp *pb.DeleteResponse) error {
   128  	// Validate the request
   129  	if len(req.Id) == 0 {
   130  		return errors.BadRequest("auth.Rules.Delete", "ID missing")
   131  	}
   132  
   133  	// set defaults
   134  	if req.Options == nil {
   135  		req.Options = &pb.Options{}
   136  	}
   137  	if len(req.Options.Namespace) == 0 {
   138  		req.Options.Namespace = namespace.DefaultNamespace
   139  	}
   140  
   141  	// authorize the request
   142  	if err := namespace.AuthorizeAdmin(ctx, req.Options.Namespace, "auth.Rules.Delete"); err != nil {
   143  		return err
   144  	}
   145  
   146  	// Delete the rule
   147  	key := strings.Join([]string{storePrefixRules, req.Options.Namespace, req.Id}, joinKey)
   148  	err := store.DefaultStore.Delete(key)
   149  	if err == store.ErrNotFound {
   150  		return errors.BadRequest("auth.Rules.Delete", "Rule not found")
   151  	} else if err != nil {
   152  		return errors.InternalServerError("auth.Rules.Delete", "Unable to delete key from store: %v", err)
   153  	}
   154  
   155  	// Clear the namespace cache, since the rules for this namespace could now be empty
   156  	r.Lock()
   157  	delete(r.namespaces, req.Options.Namespace)
   158  	r.Unlock()
   159  
   160  	return nil
   161  }
   162  
   163  // List returns all the rules
   164  func (r *Rules) List(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error {
   165  	// set defaults
   166  	if req.Options == nil {
   167  		req.Options = &pb.Options{}
   168  	}
   169  	if len(req.Options.Namespace) == 0 {
   170  		req.Options.Namespace = namespace.DefaultNamespace
   171  	}
   172  
   173  	// authorize the request
   174  	if err := namespace.Authorize(ctx, req.Options.Namespace, "auth.Rules.List"); err != nil {
   175  		return err
   176  	}
   177  
   178  	// setup the defaults incase none exist
   179  	r.setupDefaultRules(req.Options.Namespace)
   180  
   181  	// get the records from the store
   182  	prefix := strings.Join([]string{storePrefixRules, req.Options.Namespace, ""}, joinKey)
   183  	recs, err := store.DefaultStore.Read(prefix, store.ReadPrefix())
   184  	if err != nil {
   185  		return errors.InternalServerError("auth.Rules.List", "Unable to read from store: %v", err)
   186  	}
   187  
   188  	// unmarshal the records
   189  	rsp.Rules = make([]*pb.Rule, 0, len(recs))
   190  	for _, rec := range recs {
   191  		var r *pb.Rule
   192  		if err := json.Unmarshal(rec.Value, &r); err != nil {
   193  			return errors.InternalServerError("auth.Rules.List", "Error to unmarshaling json: %v. Value: %v", err, string(rec.Value))
   194  		}
   195  		rsp.Rules = append(rsp.Rules, r)
   196  	}
   197  
   198  	return nil
   199  }
   200  
   201  // writeRule to the store
   202  func (r *Rules) writeRule(rule *pb.Rule, ns string) error {
   203  	key := strings.Join([]string{storePrefixRules, ns, rule.Id}, joinKey)
   204  	if _, err := store.DefaultStore.Read(key); err == nil {
   205  		return errors.BadRequest("auth.Rules.Create", "A rule with this ID already exists")
   206  	}
   207  
   208  	// Encode the rule
   209  	bytes, err := json.Marshal(rule)
   210  	if err != nil {
   211  		return errors.InternalServerError("auth.Rules.Create", "Unable to marshal rule: %v", err)
   212  	}
   213  
   214  	// Write to the store
   215  	if err := store.DefaultStore.Write(&store.Record{Key: key, Value: bytes}); err != nil {
   216  		return errors.InternalServerError("auth.Rules.Create", "Unable to write to the store: %v", err)
   217  	}
   218  
   219  	return nil
   220  }