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  }