github.com/kubewharf/katalyst-core@v0.5.3/pkg/util/credential/basic.go (about) 1 /* 2 Copyright 2022 The Katalyst Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package credential 18 19 import ( 20 "context" 21 "encoding/base64" 22 "fmt" 23 "net/http" 24 "strings" 25 "sync" 26 "time" 27 28 "k8s.io/apimachinery/pkg/util/wait" 29 30 "github.com/kubewharf/katalyst-core/pkg/config/agent/dynamic" 31 "github.com/kubewharf/katalyst-core/pkg/config/generic" 32 "github.com/kubewharf/katalyst-core/pkg/util/general" 33 ) 34 35 const ( 36 secretSyncInterval = 30 * time.Second 37 ) 38 39 type BasicAuthInfo struct { 40 Username string 41 Password string 42 } 43 44 func (b BasicAuthInfo) AuthType() AuthType { 45 return AuthTypeBasicAuth 46 } 47 48 func (b BasicAuthInfo) SubjectName() string { 49 return b.Username 50 } 51 52 func NewBasicAuthCredential(_ *generic.AuthConfiguration, dynamicConfiguration *dynamic.DynamicAgentConfiguration) (Credential, error) { 53 return &basicAuthCredential{ 54 authPairs: map[string]string{}, 55 dynamicConfig: dynamicConfiguration, 56 }, nil 57 } 58 59 type basicAuthCredential struct { 60 mutex sync.RWMutex 61 dynamicConfig *dynamic.DynamicAgentConfiguration 62 authPairs map[string]string 63 } 64 65 func (b *basicAuthCredential) Run(ctx context.Context) { 66 go wait.Until(b.updateAuthPairFromDynamicConf, secretSyncInterval, ctx.Done()) 67 } 68 69 func (b *basicAuthCredential) AuthType() AuthType { 70 return AuthTypeBasicAuth 71 } 72 73 func (b *basicAuthCredential) Auth(r *http.Request) (AuthInfo, error) { 74 username, password, ok := r.BasicAuth() 75 76 if !ok { 77 return nil, fmt.Errorf("invalid basic auth token:%v", r.Header.Get("Authorization")) 78 } 79 80 err := b.verifyAuthInfo(username, password) 81 if err != nil { 82 return nil, err 83 } 84 85 return BasicAuthInfo{Username: username, Password: password}, nil 86 } 87 88 func (b *basicAuthCredential) AuthToken(token string) (AuthInfo, error) { 89 username, password, ok := parseBasicAuth(token) 90 if !ok { 91 return nil, fmt.Errorf("invalid basic auth token:%v", token) 92 } 93 94 err := b.verifyAuthInfo(username, password) 95 if err != nil { 96 return nil, err 97 } 98 99 return BasicAuthInfo{Username: username, Password: password}, nil 100 } 101 102 func (b *basicAuthCredential) verifyAuthInfo(username, password string) error { 103 b.mutex.RLock() 104 defer b.mutex.RUnlock() 105 106 storedPassword, ok := b.authPairs[username] 107 if !ok { 108 return fmt.Errorf("user %v not found in store", username) 109 } 110 if storedPassword != password { 111 return fmt.Errorf("password for user %v are wrong", username) 112 } 113 114 return nil 115 } 116 117 // parseBasicAuth parses an HTTP Basic Authentication string. 118 // "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true). 119 func parseBasicAuth(auth string) (username, password string, ok bool) { 120 const prefix = "Basic " 121 // Case-insensitive prefix match. 122 if len(auth) < len(prefix) || !strings.EqualFold(strings.ToLower(auth[:len(prefix)]), strings.ToLower(prefix)) { 123 return "", "", false 124 } 125 c, err := base64.StdEncoding.DecodeString(auth[len(prefix):]) 126 if err != nil { 127 return "", "", false 128 } 129 cs := string(c) 130 username, password, ok = strings.Cut(cs, ":") 131 if !ok { 132 return "", "", false 133 } 134 return username, password, true 135 } 136 137 func (b *basicAuthCredential) updateAuthPairFromDynamicConf() { 138 dynamicConfiguration := b.dynamicConfig.GetDynamicConfiguration() 139 newAuthPairs := make(map[string]string) 140 for _, pair := range dynamicConfiguration.UserPasswordPairs { 141 p, err := base64.StdEncoding.DecodeString(pair.Password) 142 if err != nil { 143 general.Warningf("fail to decode password, err: %v", err) 144 continue 145 } 146 newAuthPairs[pair.Username] = string(p) 147 } 148 b.mutex.Lock() 149 b.authPairs = newAuthPairs 150 b.mutex.Unlock() 151 }