github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libpages/config/config_v1.go (about) 1 // Copyright 2017 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package config 6 7 import ( 8 "context" 9 "encoding/json" 10 "io" 11 "strings" 12 "sync" 13 "time" 14 15 "golang.org/x/time/rate" 16 ) 17 18 // V1 defines a V1 config. Public fields are accessible by `json` 19 // encoders and decoder. 20 // 21 // On first call to GetPermission* methods, it initializes an internal per-path 22 // config reader. If the object is constructed from ParseConfig, its internal 23 // per-path config reader is initialized automatically. Any changes to the 24 // PerPathConfigs fields afterwards have no effect. 25 type V1 struct { 26 Common 27 28 // Users is a [username -> bcrypt-hashed password] map that defines how 29 // users should be authenticated. 30 Users map[string]string `json:"users"` 31 32 users map[string]password 33 34 bcryptLimiter *rate.Limiter 35 36 // ACLs is deprecated, and kept around for back-compability. Now it serves 37 // as an alias to PerPathConfigs. If both ACLs and PerPathConfigs are 38 // present, it's a parsing error. 39 ACLs map[string]PerPathConfigV1 `json:"acls,omitempty"` 40 41 // PerPathConfigs is a path -> PerPathConfig map to configure parameters 42 // for individual paths. Configured paths apply to their sub paths too. 43 PerPathConfigs map[string]PerPathConfigV1 `json:"per_path_configs"` 44 45 initOnce sync.Once 46 perPathConfigsReader *perPathConfigsReaderV1 47 perPathConfigsReaderInitErr error 48 } 49 50 var _ Config = (*V1)(nil) 51 52 // DefaultV1 returns a default V1 config, which allows anonymous read to 53 // everything. 54 func DefaultV1() *V1 { 55 v1 := &V1{ 56 Common: Common{ 57 Version: Version1Str, 58 }, 59 PerPathConfigs: map[string]PerPathConfigV1{ 60 "/": { 61 AnonymousPermissions: "read", 62 }, 63 }, 64 } 65 _ = v1.EnsureInit() // TODO: check error? 66 return v1 67 } 68 69 const bcryptRateLimitInterval = time.Second / 2 70 71 func (c *V1) checkAndRenameACLsIfNeeded() error { 72 if c.PerPathConfigs != nil && c.ACLs != nil { 73 return ErrACLsPerPathConfigsBothPresent{} 74 } 75 if c.ACLs != nil { 76 c.PerPathConfigs = c.ACLs 77 c.ACLs = nil 78 } 79 return nil 80 } 81 82 func (c *V1) init() { 83 c.bcryptLimiter = rate.NewLimiter(rate.Every(bcryptRateLimitInterval), 1) 84 85 c.perPathConfigsReaderInitErr = c.checkAndRenameACLsIfNeeded() 86 if c.perPathConfigsReaderInitErr != nil { 87 return 88 } 89 c.perPathConfigsReader, c.perPathConfigsReaderInitErr = 90 makePerPathConfigsReaderV1(c.PerPathConfigs, c.Users) 91 if c.perPathConfigsReaderInitErr != nil { 92 return 93 } 94 95 c.users = make(map[string]password) 96 for username, passwordHash := range c.Users { 97 c.users[username], c.perPathConfigsReaderInitErr = newPassword(passwordHash) 98 if c.perPathConfigsReaderInitErr != nil { 99 return 100 } 101 } 102 } 103 104 // EnsureInit initializes c, and returns any error encountered during the 105 // initialization. Additionally, it also moves ACLs into PerPathConfigs if 106 // needed. 107 // 108 // It is not necessary to call EnsureInit. Methods that need it do it 109 // automatically. 110 func (c *V1) EnsureInit() error { 111 c.initOnce.Do(c.init) 112 return c.perPathConfigsReaderInitErr 113 } 114 115 // Version implements the Config interface. 116 func (c *V1) Version() Version { 117 return Version1 118 } 119 120 // Authenticate implements the Config interface. 121 func (c *V1) Authenticate(ctx context.Context, username, cleartextPassword string) bool { 122 if c.EnsureInit() != nil { 123 return false 124 } 125 126 p, ok := c.users[username] 127 if !ok { 128 return false 129 } 130 match, err := p.check(ctx, c.bcryptLimiter, cleartextPassword) 131 return err == nil && match 132 } 133 134 // GetPermissions implements the Config interface. 135 func (c *V1) GetPermissions(path string, username *string) ( 136 read, list bool, 137 possibleRead, possibleList bool, 138 realm string, err error) { 139 if err = c.EnsureInit(); err != nil { 140 return false, false, false, false, "", err 141 } 142 143 perms, maxPerms, realm := c.perPathConfigsReader.getPermissions(path, username) 144 return perms.read, perms.list, maxPerms.read, maxPerms.list, realm, nil 145 } 146 147 // GetAccessControlAllowOrigin implements the Config interface. 148 func (c *V1) GetAccessControlAllowOrigin(path string) (setting string, err error) { 149 if err = c.EnsureInit(); err != nil { 150 return "", err 151 } 152 return c.perPathConfigsReader.getSetAccessControlAllowOrigin(path), nil 153 } 154 155 // Encode implements the Config interface. 156 func (c *V1) Encode(w io.Writer, prettify bool) error { 157 encoder := json.NewEncoder(w) 158 if prettify { 159 encoder.SetIndent("", strings.Repeat(" ", 2)) 160 } 161 return encoder.Encode(c) 162 } 163 164 // Validate checks all public fields of c, and returns an error if any of them 165 // is invalid, or a nil-error if they are all valid. 166 // 167 // Although changes to per-path config fields have no effect on per-path config 168 // checkings once the internal per-path config reader is intialized (see 169 // comment on V1), this method still checks the updated per-path config fields. 170 // So it's OK to use Validate directly on a *V1 that has been modified since it 171 // was initialized. 172 // 173 // As a result, unlike other methods on the type, this method is not goroutine 174 // safe against changes to the public fields. 175 func (c *V1) Validate() (err error) { 176 if err := c.checkAndRenameACLsIfNeeded(); err != nil { 177 return err 178 } 179 _, err = makePerPathConfigsReaderV1(c.PerPathConfigs, c.Users) 180 return err 181 } 182 183 // HasBcryptPasswords checks if any password hash in the config is a bcrypt 184 // hash. This method is temporary for migration and will go away. 185 func (c *V1) HasBcryptPasswords() (bool, error) { 186 if err := c.EnsureInit(); err != nil { 187 return false, err 188 } 189 for _, pass := range c.users { 190 if pass.passwordType() == passwordTypeBcrypt { 191 return true, nil 192 } 193 } 194 return false, nil 195 }