...

Source file src/github.com/go-kivik/kivik/v4/x/server/auth/userstore.go

Documentation: github.com/go-kivik/kivik/v4/x/server/auth

     1  // Licensed under the Apache License, Version 2.0 (the "License"); you may not
     2  // use this file except in compliance with the License. You may obtain a copy of
     3  // the License at
     4  //
     5  //  http://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     9  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    10  // License for the specific language governing permissions and limitations under
    11  // the License.
    12  
    13  package auth
    14  
    15  import (
    16  	"context"
    17  	"crypto/rand"
    18  	"math/big"
    19  	"net/http"
    20  	"sync"
    21  
    22  	internal "github.com/go-kivik/kivik/v4/int/errors"
    23  )
    24  
    25  // A UserStore provides an AuthHandler with access to a user store for.
    26  type UserStore interface {
    27  	// Validate returns a user context object if the credentials are valid. An
    28  	// error must be returned otherwise. A Not-Found error will continue to the
    29  	// next user store, while any other error will terminate the auth process.
    30  	Validate(ctx context.Context, username, password string) (user *UserContext, err error)
    31  	// UserCtx returns a user context object if the user exists. It is used by
    32  	// AuthHandlers that don't validate the password (e.g. Cookie auth). If the
    33  	// user does not exist, a Not-Found error will be returned.
    34  	UserCtx(ctx context.Context, username string) (user *UserContext, err error)
    35  }
    36  
    37  // MemoryUserStore is a simple in-memory user store.
    38  type MemoryUserStore struct {
    39  	users sync.Map
    40  }
    41  
    42  var _ UserStore = (*MemoryUserStore)(nil)
    43  
    44  type memoryUser struct {
    45  	Salt     string
    46  	Password string
    47  	Roles    []string
    48  }
    49  
    50  // NewMemoryUserStore returns a new MemoryUserStore.
    51  func NewMemoryUserStore() *MemoryUserStore {
    52  	return &MemoryUserStore{}
    53  }
    54  
    55  // AddUser adds a user to the store. It returns an error if the user already
    56  // exists.
    57  func (s *MemoryUserStore) AddUser(username, password string, roles []string) error {
    58  	salt, err := generateSalt()
    59  	if err != nil {
    60  		return err
    61  	}
    62  	_, loaded := s.users.LoadOrStore(username, &memoryUser{
    63  		Salt:     salt,
    64  		Password: password,
    65  		Roles:    roles,
    66  	})
    67  	if loaded {
    68  		return &internal.Error{Status: http.StatusConflict, Message: "User already exists"}
    69  	}
    70  	return nil
    71  }
    72  
    73  func generateSalt() (string, error) {
    74  	const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    75  	const n = 16
    76  	ret := make([]byte, n)
    77  	for i := 0; i < n; i++ {
    78  		num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
    79  		if err != nil {
    80  			return "", err
    81  		}
    82  		ret[i] = letters[num.Int64()]
    83  	}
    84  
    85  	return string(ret), nil
    86  }
    87  
    88  // DeleteUser deletes a user from the store.
    89  func (s *MemoryUserStore) DeleteUser(username string) {
    90  	s.users.Delete(username)
    91  }
    92  
    93  var (
    94  	errNotFound     = &internal.Error{Status: http.StatusNotFound, Message: "User not found"}
    95  	errUnauthorized = &internal.Error{Status: http.StatusUnauthorized, Message: "Invalid username or password"}
    96  )
    97  
    98  // Validate returns a user context object if the credentials are valid.
    99  func (s *MemoryUserStore) Validate(_ context.Context, username, password string) (*UserContext, error) {
   100  	user, ok := s.users.Load(username)
   101  	if !ok {
   102  		return nil, errNotFound
   103  	}
   104  	if user.(*memoryUser).Password != password {
   105  		return nil, errUnauthorized
   106  	}
   107  	return &UserContext{
   108  		Name:  username,
   109  		Salt:  user.(*memoryUser).Salt,
   110  		Roles: user.(*memoryUser).Roles,
   111  	}, nil
   112  }
   113  
   114  // UserCtx returns a user context object if the user exists.
   115  func (s *MemoryUserStore) UserCtx(_ context.Context, username string) (*UserContext, error) {
   116  	user, ok := s.users.Load(username)
   117  	if !ok {
   118  		return nil, errNotFound
   119  	}
   120  	return &UserContext{
   121  		Name:  username,
   122  		Salt:  user.(*memoryUser).Salt,
   123  		Roles: user.(*memoryUser).Roles,
   124  	}, nil
   125  }
   126  

View as plain text