...

Source file src/github.com/go-kivik/kivik/v4/x/kivikd/authdb/usersdb/usersdb.go

Documentation: github.com/go-kivik/kivik/v4/x/kivikd/authdb/usersdb

     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 usersdb provides auth facilities from a CouchDB _users database.
    14  package usersdb
    15  
    16  import (
    17  	"context"
    18  	"crypto/sha1"
    19  	"errors"
    20  	"fmt"
    21  	"net/http"
    22  
    23  	"golang.org/x/crypto/pbkdf2"
    24  
    25  	"github.com/go-kivik/kivik/v4"
    26  	internal "github.com/go-kivik/kivik/v4/int/errors"
    27  	"github.com/go-kivik/kivik/v4/x/kivikd/authdb"
    28  )
    29  
    30  type db struct {
    31  	*kivik.DB
    32  }
    33  
    34  var _ authdb.UserStore = &db{}
    35  
    36  // New returns a new authdb.UserStore backed by a the provided database.
    37  func New(userDB *kivik.DB) authdb.UserStore {
    38  	return &db{userDB}
    39  }
    40  
    41  type user struct {
    42  	Name           string   `json:"name"`
    43  	Roles          []string `json:"roles"`
    44  	PasswordScheme string   `json:"password_scheme,omitempty"`
    45  	Salt           string   `json:"salt,omitempty"`
    46  	Iterations     int      `json:"iterations,omitempty"`
    47  	DerivedKey     string   `json:"derived_key,omitempty"`
    48  }
    49  
    50  func (db *db) getUser(ctx context.Context, username string) (*user, error) {
    51  	var u user
    52  	if err := db.Get(ctx, kivik.UserPrefix+username, nil).ScanDoc(&u); err != nil {
    53  		return nil, err
    54  	}
    55  	return &u, nil
    56  }
    57  
    58  func (db *db) Validate(ctx context.Context, username, password string) (*authdb.UserContext, error) {
    59  	u, err := db.getUser(ctx, username)
    60  	if err != nil {
    61  		if kivik.HTTPStatus(err) == http.StatusNotFound {
    62  			err = &internal.Error{Status: http.StatusUnauthorized, Message: "unauthorized"}
    63  		}
    64  		return nil, err
    65  	}
    66  
    67  	switch u.PasswordScheme {
    68  	case "":
    69  		return nil, errors.New("no password scheme set for user")
    70  	case authdb.SchemePBKDF2:
    71  	default:
    72  		return nil, fmt.Errorf("unsupported password scheme: %s", u.PasswordScheme)
    73  	}
    74  	key := fmt.Sprintf("%x", pbkdf2.Key([]byte(password), []byte(u.Salt), u.Iterations, authdb.PBKDF2KeyLength, sha1.New))
    75  	if key != u.DerivedKey {
    76  		return nil, &internal.Error{Status: http.StatusUnauthorized, Message: "unauthorized"}
    77  	}
    78  	return &authdb.UserContext{
    79  		Name:  u.Name,
    80  		Roles: u.Roles,
    81  		Salt:  u.Salt,
    82  	}, nil
    83  }
    84  
    85  func (db *db) UserCtx(ctx context.Context, username string) (*authdb.UserContext, error) {
    86  	u, err := db.getUser(ctx, username)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	return &authdb.UserContext{
    91  		Name:  u.Name,
    92  		Roles: u.Roles,
    93  		Salt:  u.Salt,
    94  	}, nil
    95  }
    96  

View as plain text