github.com/go-kivik/kivik/v4@v4.3.2/x/kivikd/authdb/authgroup/authgroup.go (about) 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 }