github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/auth/model/model.go (about) 1 package model 2 3 import ( 4 "database/sql/driver" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "strconv" 9 "time" 10 11 "github.com/go-openapi/swag" 12 "github.com/google/uuid" 13 "github.com/treeverse/lakefs/pkg/auth/crypt" 14 "github.com/treeverse/lakefs/pkg/kv" 15 "golang.org/x/crypto/bcrypt" 16 "google.golang.org/protobuf/proto" 17 "google.golang.org/protobuf/types/known/timestamppb" 18 ) 19 20 const ( 21 StatementEffectAllow = "allow" 22 StatementEffectDeny = "deny" 23 PartitionKey = "auth" 24 groupsPrefix = "groups" 25 groupsUsersPrefix = "gUsers" 26 groupsPoliciesPrefix = "gPolicies" 27 usersPrefix = "users" 28 policiesPrefix = "policies" 29 usersPoliciesPrefix = "uPolicies" 30 usersCredentialsPrefix = "uCredentials" // #nosec G101 -- False positive: this is only a kv key prefix 31 credentialsPrefix = "credentials" 32 expiredTokensPrefix = "expiredTokens" 33 metadataPrefix = "installation_metadata" 34 ) 35 36 //nolint:gochecknoinits 37 func init() { 38 kv.MustRegisterType("auth", "users", (&UserData{}).ProtoReflect().Type()) 39 kv.MustRegisterType("auth", "policies", (&PolicyData{}).ProtoReflect().Type()) 40 kv.MustRegisterType("auth", "groups", (&GroupData{}).ProtoReflect().Type()) 41 kv.MustRegisterType("auth", kv.FormatPath("uCredentials", "*", "credentials"), (&CredentialData{}).ProtoReflect().Type()) 42 kv.MustRegisterType("auth", kv.FormatPath("gUsers", "*", "users"), (&kv.SecondaryIndex{}).ProtoReflect().Type()) 43 kv.MustRegisterType("auth", kv.FormatPath("gPolicies", "*", "policies"), (&kv.SecondaryIndex{}).ProtoReflect().Type()) 44 kv.MustRegisterType("auth", kv.FormatPath("uPolicies", "*", "policies"), (&kv.SecondaryIndex{}).ProtoReflect().Type()) 45 kv.MustRegisterType("auth", "expiredTokens", (&TokenData{}).ProtoReflect().Type()) 46 kv.MustRegisterType("auth", "installation_metadata", nil) 47 } 48 49 func UserPath(userName string) []byte { 50 return []byte(kv.FormatPath(usersPrefix, userName)) 51 } 52 53 func PolicyPath(displayName string) []byte { 54 return []byte(kv.FormatPath(policiesPrefix, displayName)) 55 } 56 57 func GroupPath(displayName string) []byte { 58 return []byte(kv.FormatPath(groupsPrefix, displayName)) 59 } 60 61 func CredentialPath(userName string, accessKeyID string) []byte { 62 return []byte(kv.FormatPath(usersCredentialsPrefix, userName, credentialsPrefix, accessKeyID)) 63 } 64 65 func GroupUserPath(groupDisplayName string, userName string) []byte { 66 return []byte(kv.FormatPath(groupsUsersPrefix, groupDisplayName, usersPrefix, userName)) 67 } 68 69 func UserPolicyPath(userName string, policyDisplayName string) []byte { 70 return []byte(kv.FormatPath(usersPoliciesPrefix, userName, policiesPrefix, policyDisplayName)) 71 } 72 73 func GroupPolicyPath(groupDisplayName string, policyDisplayName string) []byte { 74 return []byte(kv.FormatPath(groupsPoliciesPrefix, groupDisplayName, policiesPrefix, policyDisplayName)) 75 } 76 77 func ExpiredTokenPath(tokenID string) []byte { 78 return []byte(kv.FormatPath(expiredTokensPrefix, tokenID)) 79 } 80 81 func ExpiredTokensPath() []byte { 82 return ExpiredTokenPath("") 83 } 84 85 func MetadataKeyPath(key string) string { 86 return kv.FormatPath(metadataPrefix, key) 87 } 88 89 var ErrInvalidStatementSrcFormat = errors.New("invalid statements src format") 90 91 type PaginationParams struct { 92 Prefix string 93 After string 94 Amount int 95 } 96 97 // Paginator describes the parameters of a slice of data from a database. 98 type Paginator struct { 99 Amount int 100 NextPageToken string 101 } 102 103 // SuperuserConfiguration requests a particular configuration for a superuser. 104 type SuperuserConfiguration struct { 105 User 106 AccessKeyID string 107 SecretAccessKey string 108 } 109 110 type User struct { 111 CreatedAt time.Time `db:"created_at"` 112 // Username is a unique identifier for the user. In password-based authentication, it is the email. 113 Username string `db:"display_name" json:"display_name"` 114 // FriendlyName, if set, is a shorter name for the user than 115 // Username. Unlike Username it does not identify the user (it 116 // might not be unique); use it in the user's GUI rather than in 117 // backend code. 118 FriendlyName *string `db:"friendly_name" json:"friendly_name"` 119 Email *string `db:"email" json:"email"` 120 EncryptedPassword []byte `db:"encrypted_password" json:"encrypted_password"` 121 Source string `db:"source" json:"source"` 122 ExternalID *string `db:"external_id" json:"external_id"` 123 } 124 125 func (u *User) Committer() string { 126 if u.Email != nil && *u.Email != "" { 127 return *u.Email 128 } 129 return u.Username 130 } 131 132 type DBUser struct { 133 ID int64 `db:"id"` 134 User 135 } 136 137 func ConvertDBID(id int64) string { 138 const base = 10 139 return strconv.FormatInt(id, base) 140 } 141 142 type Group struct { 143 ID string `db:"id"` 144 CreatedAt time.Time `db:"created_at"` 145 DisplayName string `db:"display_name" json:"display_name"` 146 } 147 148 type DBGroup struct { 149 ID int `db:"id"` 150 Group 151 } 152 153 type ACLPermission string 154 155 type ACL struct { 156 Permission ACLPermission `json:"permission"` 157 } 158 159 type Policy struct { 160 CreatedAt time.Time `db:"created_at"` 161 DisplayName string `db:"display_name" json:"display_name"` 162 Statement Statements `db:"statement"` 163 ACL ACL `db:"acl" json:"acl,omitempty"` 164 } 165 166 type DBPolicy struct { 167 ID int `db:"id"` 168 Policy 169 } 170 171 type Statement struct { 172 Effect string `json:"Effect"` 173 Action []string `json:"Action"` 174 Resource string `json:"Resource"` 175 } 176 177 type Statements []Statement 178 179 type BaseCredential struct { 180 AccessKeyID string `db:"access_key_id"` 181 SecretAccessKey string `db:"-" json:"-"` 182 SecretAccessKeyEncryptedBytes []byte `db:"secret_access_key" json:"-"` 183 IssuedDate time.Time `db:"issued_date"` 184 } 185 186 type Credential struct { 187 Username string 188 BaseCredential 189 } 190 191 type DBCredential struct { 192 UserID int64 `db:"user_id"` 193 BaseCredential 194 } 195 196 // CredentialKeys - For JSON serialization: 197 type CredentialKeys struct { 198 AccessKeyID string `json:"access_key_id"` 199 SecretAccessKey string `json:"secret_access_key"` 200 } 201 202 // ExternalPrincipal represents an attachment of a user to an external identity such as an IAM Role ARN 203 type ExternalPrincipal struct { 204 // External Principal ID (i.e ARN) 205 ID string `json:"id"` 206 // The attached lakeFS user ID 207 UserID string `json:"user_id"` 208 } 209 210 func (u *User) UpdatePassword(password string) error { 211 pw, err := HashPassword(password) 212 if err != nil { 213 return err 214 } 215 u.EncryptedPassword = pw 216 return nil 217 } 218 219 // HashPassword generates a hashed password from a plaintext string 220 func HashPassword(password string) ([]byte, error) { 221 return bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) 222 } 223 224 // Authenticate a user from a password Returns nil on success, or an error on failure. 225 func (u *User) Authenticate(password string) error { 226 return bcrypt.CompareHashAndPassword(u.EncryptedPassword, []byte(password)) 227 } 228 229 func (s Statements) Value() (driver.Value, error) { 230 if s == nil { 231 return json.Marshal([]struct{}{}) 232 } 233 return json.Marshal(s) 234 } 235 236 func (s *Statements) Scan(src interface{}) error { 237 if src == nil { 238 return nil 239 } 240 data, ok := src.([]byte) 241 if !ok { 242 return ErrInvalidStatementSrcFormat 243 } 244 return json.Unmarshal(data, s) 245 } 246 247 func UserFromProto(pb *UserData) *User { 248 return &User{ 249 CreatedAt: pb.CreatedAt.AsTime(), 250 Username: pb.Username, 251 FriendlyName: &pb.FriendlyName, 252 Email: &pb.Email, 253 EncryptedPassword: pb.EncryptedPassword, 254 Source: pb.Source, 255 ExternalID: &pb.ExternalId, 256 } 257 } 258 259 func ProtoFromUser(u *User) *UserData { 260 return &UserData{ 261 CreatedAt: timestamppb.New(u.CreatedAt), 262 Username: u.Username, 263 FriendlyName: swag.StringValue(u.FriendlyName), 264 Email: swag.StringValue(u.Email), 265 EncryptedPassword: u.EncryptedPassword, 266 Source: u.Source, 267 ExternalId: swag.StringValue(u.ExternalID), 268 } 269 } 270 271 func GroupFromProto(pb *GroupData) *Group { 272 return &Group{ 273 CreatedAt: pb.CreatedAt.AsTime(), 274 DisplayName: pb.DisplayName, 275 ID: pb.DisplayName, 276 } 277 } 278 279 func ProtoFromGroup(g *Group) *GroupData { 280 return &GroupData{ 281 CreatedAt: timestamppb.New(g.CreatedAt), 282 DisplayName: g.DisplayName, 283 } 284 } 285 286 func PolicyFromProto(pb *PolicyData) *Policy { 287 policy := &Policy{ 288 CreatedAt: pb.CreatedAt.AsTime(), 289 DisplayName: pb.DisplayName, 290 Statement: *statementsFromProto(pb.Statements), 291 } 292 if pb.Acl != nil { 293 policy.ACL = ACL{ 294 Permission: ACLPermission(pb.Acl.Permission), 295 } 296 } 297 return policy 298 } 299 300 func ProtoFromPolicy(p *Policy) *PolicyData { 301 return &PolicyData{ 302 CreatedAt: timestamppb.New(p.CreatedAt), 303 DisplayName: p.DisplayName, 304 Statements: protoFromStatements(&p.Statement), 305 Acl: &ACLData{ 306 Permission: string(p.ACL.Permission), 307 }, 308 } 309 } 310 311 func CredentialFromProto(s crypt.SecretStore, pb *CredentialData) (*Credential, error) { 312 secret, err := DecryptSecret(s, pb.SecretAccessKeyEncryptedBytes) 313 if err != nil { 314 return nil, err 315 } 316 return &Credential{ 317 Username: string(pb.UserId), 318 BaseCredential: BaseCredential{ 319 AccessKeyID: pb.AccessKeyId, 320 SecretAccessKey: secret, 321 SecretAccessKeyEncryptedBytes: pb.SecretAccessKeyEncryptedBytes, 322 IssuedDate: pb.IssuedDate.AsTime(), 323 }, 324 }, nil 325 } 326 327 func ProtoFromCredential(c *Credential) *CredentialData { 328 return &CredentialData{ 329 AccessKeyId: c.AccessKeyID, 330 SecretAccessKeyEncryptedBytes: c.SecretAccessKeyEncryptedBytes, 331 IssuedDate: timestamppb.New(c.IssuedDate), 332 UserId: []byte(c.Username), 333 } 334 } 335 336 func statementFromProto(pb *StatementData) *Statement { 337 return &Statement{ 338 Effect: pb.Effect, 339 Action: pb.Action, 340 Resource: pb.Resource, 341 } 342 } 343 344 func protoFromStatement(s *Statement) *StatementData { 345 return &StatementData{ 346 Effect: s.Effect, 347 Action: s.Action, 348 Resource: s.Resource, 349 } 350 } 351 352 func statementsFromProto(pb []*StatementData) *Statements { 353 statements := make(Statements, len(pb)) 354 for i := range pb { 355 statements[i] = *statementFromProto(pb[i]) 356 } 357 return &statements 358 } 359 360 func protoFromStatements(s *Statements) []*StatementData { 361 statements := make([]*StatementData, len(*s)) 362 x := *s 363 for i := range *s { 364 statements[i] = protoFromStatement(&x[i]) 365 } 366 return statements 367 } 368 369 func ConvertUsersDataList(users []proto.Message) []*User { 370 kvUsers := make([]*User, 0, len(users)) 371 for _, u := range users { 372 a := u.(*UserData) 373 kvUsers = append(kvUsers, UserFromProto(a)) 374 } 375 return kvUsers 376 } 377 378 func ConvertGroupDataList(group []proto.Message) []*Group { 379 res := make([]*Group, 0, len(group)) 380 for _, g := range group { 381 a := g.(*GroupData) 382 res = append(res, GroupFromProto(a)) 383 } 384 return res 385 } 386 387 func ConvertPolicyDataList(policies []proto.Message) []*Policy { 388 res := make([]*Policy, 0, len(policies)) 389 for _, p := range policies { 390 a := p.(*PolicyData) 391 res = append(res, PolicyFromProto(a)) 392 } 393 return res 394 } 395 396 func ConvertCredDataList(s crypt.SecretStore, creds []proto.Message) ([]*Credential, error) { 397 res := make([]*Credential, 0, len(creds)) 398 for _, c := range creds { 399 credentialData := c.(*CredentialData) 400 m, err := CredentialFromProto(s, credentialData) 401 if err != nil { 402 return nil, fmt.Errorf("credentials for %s: %w", credentialData.AccessKeyId, err) 403 } 404 m.SecretAccessKey = "" 405 res = append(res, m) 406 } 407 return res, nil 408 } 409 410 func DecryptSecret(s crypt.SecretStore, value []byte) (string, error) { 411 decrypted, err := s.Decrypt(value) 412 if err != nil { 413 return "", err 414 } 415 return string(decrypted), nil 416 } 417 418 func EncryptSecret(s crypt.SecretStore, secretAccessKey string) ([]byte, error) { 419 encrypted, err := s.Encrypt([]byte(secretAccessKey)) 420 if err != nil { 421 return nil, err 422 } 423 return encrypted, nil 424 } 425 426 func CreateID() string { 427 return uuid.New().String() 428 }