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 }