...

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

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

     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 authgroup
    16  
    17  import (
    18  	"context"
    19  	"net/http"
    20  
    21  	"github.com/go-kivik/kivik/v4"
    22  	internal "github.com/go-kivik/kivik/v4/int/errors"
    23  	"github.com/go-kivik/kivik/v4/x/kivikd/authdb"
    24  )
    25  
    26  // AuthGroup is a group of auth handlers, to be tried in turn.
    27  type AuthGroup []authdb.UserStore
    28  
    29  var _ authdb.UserStore = AuthGroup{}
    30  
    31  // New initializes a group of auth handlers. Each one is tried in turn, in the
    32  // order passed to New.
    33  func New(userStores ...authdb.UserStore) authdb.UserStore {
    34  	return append(AuthGroup{}, userStores...)
    35  }
    36  
    37  func (g AuthGroup) loop(ctx context.Context, fn func(authdb.UserStore) (*authdb.UserContext, error)) (*authdb.UserContext, error) {
    38  	var firstErr error
    39  	for _, store := range g {
    40  		uCtx, err := fn(store)
    41  		if err == nil {
    42  			return uCtx, nil
    43  		}
    44  		if kivik.HTTPStatus(err) != http.StatusNotFound && firstErr == nil {
    45  			firstErr = err
    46  		}
    47  		select {
    48  		// See if our context has expired
    49  		case <-ctx.Done():
    50  			return nil, ctx.Err()
    51  		default:
    52  		}
    53  	}
    54  	if firstErr == nil {
    55  		return nil, &internal.Error{Status: http.StatusNotFound, Message: "user not found"}
    56  	}
    57  	return nil, firstErr
    58  }
    59  
    60  // Validate loops through each of the auth handlers, in the order passed to New,
    61  // until the context is cancelled, in which case the context's error is returned.
    62  // The first validation success returns. Errors are discarded unless all auth
    63  // handlers fail to validate the user, in which case only the first error
    64  // received will be returned.
    65  func (g AuthGroup) Validate(ctx context.Context, username, password string) (*authdb.UserContext, error) {
    66  	return g.loop(ctx, func(store authdb.UserStore) (*authdb.UserContext, error) {
    67  		return store.Validate(ctx, username, password)
    68  	})
    69  }
    70  
    71  // UserCtx loops through each of the auth handlers, in the order passed to New
    72  // until the context is cancelled, in which case the context's error is returned.
    73  // The first one to not return an error returns. If all of the handlers return
    74  // a Not Found error, Not Found is returned. If any other errors are returned,
    75  // the first is returned to the caller.
    76  func (g AuthGroup) UserCtx(ctx context.Context, username string) (*authdb.UserContext, error) {
    77  	return g.loop(ctx, func(store authdb.UserStore) (*authdb.UserContext, error) {
    78  		return store.UserCtx(ctx, username)
    79  	})
    80  }
    81  

View as plain text