...

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

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

     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  //go:build !js
    14  
    15  package confadmin
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"net/http"
    22  	"strconv"
    23  	"strings"
    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  	"github.com/go-kivik/kivik/v4/x/kivikd/conf"
    29  )
    30  
    31  type confadmin struct {
    32  	*conf.Conf
    33  }
    34  
    35  var _ authdb.UserStore = &confadmin{}
    36  
    37  // New returns a new confadmin authentication service provider.
    38  func New(c *conf.Conf) authdb.UserStore {
    39  	return &confadmin{c}
    40  }
    41  
    42  func (c *confadmin) Validate(_ context.Context, username, password string) (*authdb.UserContext, error) {
    43  	derivedKey, salt, iterations, err := c.getKeySaltIter(username)
    44  	if err != nil {
    45  		if kivik.HTTPStatus(err) == http.StatusNotFound {
    46  			return nil, &internal.Error{Status: http.StatusUnauthorized, Message: "unauthorized"}
    47  		}
    48  		return nil, fmt.Errorf("unrecognized password hash: %w", err)
    49  	}
    50  	if !authdb.ValidatePBKDF2(password, salt, derivedKey, iterations) {
    51  		return nil, &internal.Error{Status: http.StatusUnauthorized, Message: "unauthorized"}
    52  	}
    53  	return &authdb.UserContext{
    54  		Name:  username,
    55  		Roles: []string{"_admin"},
    56  		Salt:  salt,
    57  	}, nil
    58  }
    59  
    60  const hashPrefix = "-" + authdb.SchemePBKDF2 + "-"
    61  
    62  func (c *confadmin) getKeySaltIter(username string) (key, salt string, iterations int, err error) {
    63  	confName := "admins." + username
    64  	if !c.IsSet(confName) {
    65  		return "", "", 0, &internal.Error{Status: http.StatusNotFound, Message: "user not found"}
    66  	}
    67  	hash := c.GetString(confName)
    68  	if !strings.HasPrefix(hash, hashPrefix) {
    69  		return "", "", 0, errors.New("unrecognized password scheme")
    70  	}
    71  	const partsWanted = 3
    72  	parts := strings.Split(strings.TrimPrefix(hash, hashPrefix), ",")
    73  	if len(parts) != partsWanted {
    74  		return "", "", 0, errors.New("unrecognized hash format")
    75  	}
    76  	if iterations, err = strconv.Atoi(parts[2]); err != nil {
    77  		return "", "", 0, errors.New("unrecognized has format")
    78  	}
    79  	return parts[0], parts[1], iterations, nil
    80  }
    81  
    82  func (c *confadmin) UserCtx(_ context.Context, username string) (*authdb.UserContext, error) {
    83  	_, salt, _, err := c.getKeySaltIter(username)
    84  	if err != nil {
    85  		if kivik.HTTPStatus(err) == http.StatusNotFound {
    86  			return nil, &internal.Error{Status: http.StatusNotFound, Message: "user does not exist"}
    87  		}
    88  		return nil, fmt.Errorf("unrecognized password hash: %w", err)
    89  	}
    90  	return &authdb.UserContext{
    91  		Name:  username,
    92  		Roles: []string{"_admin"},
    93  		Salt:  salt,
    94  	}, nil
    95  }
    96  

View as plain text