github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/auth/handler/accounts.go (about) 1 package handler 2 3 import ( 4 "context" 5 "encoding/json" 6 "strings" 7 8 pb "github.com/tickoalcantara12/micro/v3/proto/auth" 9 "github.com/tickoalcantara12/micro/v3/service/auth" 10 "github.com/tickoalcantara12/micro/v3/service/errors" 11 "github.com/tickoalcantara12/micro/v3/service/store" 12 "github.com/tickoalcantara12/micro/v3/util/auth/namespace" 13 ) 14 15 // List returns all auth accounts 16 func (a *Auth) List(ctx context.Context, req *pb.ListAccountsRequest, rsp *pb.ListAccountsResponse) error { 17 // set defaults 18 if req.Options == nil { 19 req.Options = &pb.Options{} 20 } 21 if len(req.Options.Namespace) == 0 { 22 req.Options.Namespace = namespace.DefaultNamespace 23 } 24 25 // setup the defaults incase none exist 26 a.setupDefaultAccount(req.Options.Namespace) 27 28 // authorize the request 29 if err := namespace.AuthorizeAdmin(ctx, req.Options.Namespace, "auth.Accounts.List"); err != nil { 30 return err 31 } 32 33 // get the records from the store 34 key := strings.Join([]string{storePrefixAccounts, req.Options.Namespace, ""}, joinKey) 35 recs, err := a.Options.Store.Read(key, store.ReadPrefix()) 36 if err != nil { 37 return errors.InternalServerError("auth.Accounts.List", "Unable to read from store: %v", err) 38 } 39 40 // unmarshal the records 41 var accounts = make([]*auth.Account, 0, len(recs)) 42 for _, rec := range recs { 43 var r *auth.Account 44 if err := json.Unmarshal(rec.Value, &r); err != nil { 45 return errors.InternalServerError("auth.Accounts.List", "Error to unmarshaling json: %v. Value: %v", err, string(rec.Value)) 46 } 47 accounts = append(accounts, r) 48 } 49 50 // serialize the accounts 51 rsp.Accounts = make([]*pb.Account, 0, len(recs)) 52 for _, a := range accounts { 53 rsp.Accounts = append(rsp.Accounts, serializeAccount(a)) 54 } 55 56 return nil 57 } 58 59 // Delete an auth account 60 func (a *Auth) Delete(ctx context.Context, req *pb.DeleteAccountRequest, rsp *pb.DeleteAccountResponse) error { 61 // validate the request 62 if len(req.Id) == 0 { 63 return errors.BadRequest("auth.Accounts.Delete", "Missing ID") 64 } 65 66 acc, ok := auth.AccountFromContext(ctx) 67 if !ok { 68 return errors.Unauthorized("auth.Accounts.Delete", "Unauthorized") 69 } 70 71 // set defaults 72 if req.Options == nil { 73 req.Options = &pb.Options{} 74 } 75 if len(req.Options.Namespace) == 0 { 76 req.Options.Namespace = namespace.DefaultNamespace 77 } 78 79 // authorize the request can access this namespace 80 if err := namespace.AuthorizeAdmin(ctx, req.Options.Namespace, "auth.Accounts.Delete"); err != nil { 81 return err 82 } 83 84 // check the account exists 85 accToDelete, err := a.getAccountForID(req.Id, req.Options.Namespace, "auth.Accounts.Delete") 86 if err != nil { 87 return err 88 } 89 90 if req.Id == acc.ID || req.Id == acc.Name { 91 return errors.BadRequest("auth.Accounts.Delete", "Can't delete your own account") 92 } 93 94 // delete the refresh token linked to the account 95 tok, err := a.refreshTokenForAccount(req.Options.Namespace, accToDelete.ID) 96 if err != nil { 97 return errors.InternalServerError("auth.Accounts.Delete", "Error finding refresh token") 98 } 99 refreshKey := strings.Join([]string{storePrefixRefreshTokens, req.Options.Namespace, accToDelete.ID, tok}, joinKey) 100 if err := a.Options.Store.Delete(refreshKey); err != nil { 101 return errors.InternalServerError("auth.Accounts.Delete", "Error deleting refresh token: %v", err) 102 } 103 104 key := strings.Join([]string{storePrefixAccounts, req.Options.Namespace, accToDelete.ID}, joinKey) 105 // delete the account 106 if err := a.Options.Store.Delete(key); err != nil { 107 return errors.BadRequest("auth.Accounts.Delete", "Error deleting account: %v", err) 108 } 109 keyByName := strings.Join([]string{storePrefixAccountsByName, req.Options.Namespace, accToDelete.Name}, joinKey) 110 // delete the account 111 if err := a.Options.Store.Delete(keyByName); err != nil { 112 return errors.BadRequest("auth.Accounts.Delete", "Error deleting account: %v", err) 113 } 114 115 // Clear the namespace cache, since the accounts for this namespace could now be empty 116 a.Lock() 117 delete(a.namespaces, req.Options.Namespace) 118 a.Unlock() 119 120 return nil 121 } 122 123 func hasScope(scope string, scopes []string) bool { 124 for _, s := range scopes { 125 if scope == s { 126 return true 127 } 128 } 129 return false 130 } 131 132 // ChangeSecret by providing a refresh token and a new secret 133 func (a *Auth) ChangeSecret(ctx context.Context, req *pb.ChangeSecretRequest, rsp *pb.ChangeSecretResponse) error { 134 if len(req.NewSecret) == 0 { 135 return errors.BadRequest("auth.Auth.ChangeSecret", "New secret should not be blank") 136 } 137 138 // set defaults 139 if req.Options == nil { 140 req.Options = &pb.Options{} 141 } 142 if len(req.Options.Namespace) == 0 { 143 req.Options.Namespace = namespace.DefaultNamespace 144 } 145 146 // authorize the request 147 if err := namespace.Authorize(ctx, req.Options.Namespace, "auth.Accounts.ChangeSecret"); err != nil { 148 return err 149 } 150 151 acc, err := a.getAccountForID(req.Id, req.Options.Namespace, "auth.Accounts.ChangeSecret") 152 if err != nil { 153 return err 154 } 155 156 callerAcc, ok := auth.AccountFromContext(ctx) 157 if !ok { 158 return errors.Unauthorized("auth.Accounts.ChangeSecret", "Unauthorized") 159 } 160 161 isAdmin := func(scopes []string) bool { 162 for _, scope := range scopes { 163 if scope == "admin" { 164 return true 165 } 166 } 167 return false 168 } 169 // If the caller (acc) is a micro namespace admin account 170 // or the caller is a service, do not require the knowledge of the previous secret. 171 // This will enable both micro admins and services to change secrets on behalf of their users. 172 if !((callerAcc.Issuer == namespace.DefaultNamespace && isAdmin(callerAcc.Scopes)) || (callerAcc.Type == "service" && callerAcc.Issuer == req.Options.Namespace)) { 173 if !secretsMatch(acc.Secret, req.OldSecret) { 174 return errors.BadRequest("auth.Accounts.ChangeSecret", "Secret not correct") 175 } 176 } 177 178 // hash the secret 179 secret, err := hashSecret(req.NewSecret) 180 if err != nil { 181 return errors.InternalServerError("auth.Accounts.ChangeSecret", "Unable to hash password: %v", err) 182 } 183 acc.Secret = secret 184 185 // marshal to json 186 bytes, err := json.Marshal(acc) 187 if err != nil { 188 return errors.InternalServerError("auth.Accounts.ChangeSecret", "Unable to marshal json: %v", err) 189 } 190 191 key := strings.Join([]string{storePrefixAccounts, acc.Issuer, acc.ID}, joinKey) 192 // write to the store 193 if err := a.Options.Store.Write(&store.Record{Key: key, Value: bytes}); err != nil { 194 return errors.InternalServerError("auth.Accounts.ChangeSecret", "Unable to write account to store: %v", err) 195 } 196 usernameKey := strings.Join([]string{storePrefixAccountsByName, acc.Issuer, acc.Name}, joinKey) 197 if err := a.Options.Store.Write(&store.Record{Key: usernameKey, Value: bytes}); err != nil { 198 return errors.InternalServerError("auth.Accounts.ChangeSecret", "Unable to write account to store: %v", err) 199 } 200 201 return nil 202 } 203 204 func serializeAccount(a *auth.Account) *pb.Account { 205 return &pb.Account{ 206 Id: a.ID, 207 Type: a.Type, 208 Scopes: a.Scopes, 209 Issuer: a.Issuer, 210 Metadata: a.Metadata, 211 Name: a.Name, 212 } 213 }