mirror of
https://github.com/hanzoai/s3.git
synced 2026-10-30 14:59:09 +00:00
hanzoai/s3 was an AGPL-3.0 MinIO fork whose network-copyleft blocks embedding
and reselling as a managed service. Replace it wholesale with a fork of
SeaweedFS (github.com/seaweedfs/seaweedfs, Apache-2.0, Go) which ships a
built-in S3 gateway + Filer.
- License: AGPL-3.0 -> Apache-2.0 (upstream LICENSE kept verbatim, incl.
"Copyright 2025 Chris Lu"); NOTICE added crediting SeaweedFS. No AGPL/BUSL/
SSPL/GPL or enterprise ("ee") files remain.
- Module: go.mod path set to github.com/hanzoai/s3 with a self-replace
(replace github.com/seaweedfs/seaweedfs => ./) so the ~1800 internal Go files
keep their upstream import paths with zero churn. Internal identifiers and
import paths are intentionally NOT renamed.
- CLI rebrand: the binary is built as `s3` (was `weed`); root usage, per-command
help/usage/flag strings, shell autocomplete program name, and the product name
in CLI output now read "Hanzo S3" / `s3`. Users never see `weed`.
(e.g. `s3 server`, `s3 filer`, `s3 master`, `s3 volume`, `s3 s3`, `s3 mount`.)
- Build: `make` (or `go build -o s3 ./weed`) builds the `s3` binary green;
`cmd/weed-db` and `cmd/weed-sql` also build; `go vet ./weed/command/` clean.
- README/LLM.md rewritten for Hanzo S3; AGENTS.md/CLAUDE.md symlinks preserved.
- Dropped a 33MB prebuilt Mach-O `stress` binary upstream had committed; `.gitignore`
updated to ignore the `s3` build output.
188 lines
8.6 KiB
Go
188 lines
8.6 KiB
Go
package credential
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
)
|
|
|
|
var (
|
|
ErrUserNotFound = errors.New("user not found")
|
|
ErrUserAlreadyExists = errors.New("user already exists")
|
|
ErrAccessKeyNotFound = errors.New("access key not found")
|
|
ErrServiceAccountNotFound = errors.New("service account not found")
|
|
ErrPolicyNotFound = errors.New("policy not found")
|
|
ErrPolicyAlreadyAttached = errors.New("policy already attached")
|
|
ErrPolicyNotAttached = errors.New("policy not attached to user")
|
|
ErrGroupNotFound = errors.New("group not found")
|
|
ErrGroupAlreadyExists = errors.New("group already exists")
|
|
ErrGroupNotEmpty = errors.New("group is not empty")
|
|
ErrUserNotInGroup = errors.New("user is not a member of the group")
|
|
)
|
|
|
|
// CredentialStoreTypeName represents the type name of a credential store
|
|
type CredentialStoreTypeName string
|
|
|
|
// Credential store name constants
|
|
const (
|
|
StoreTypeMemory CredentialStoreTypeName = "memory"
|
|
StoreTypeFilerEtc CredentialStoreTypeName = "filer_etc"
|
|
StoreTypePostgres CredentialStoreTypeName = "postgres"
|
|
StoreTypeGrpc CredentialStoreTypeName = "grpc"
|
|
)
|
|
|
|
// CredentialStore defines the interface for user credential storage and retrieval
|
|
type CredentialStore interface {
|
|
// GetName returns the name of the credential store implementation
|
|
GetName() CredentialStoreTypeName
|
|
|
|
// Initialize initializes the credential store with configuration
|
|
Initialize(configuration util.Configuration, prefix string) error
|
|
|
|
// LoadConfiguration loads the entire S3 API configuration
|
|
LoadConfiguration(ctx context.Context) (*iam_pb.S3ApiConfiguration, error)
|
|
|
|
// SaveConfiguration saves the entire S3 API configuration
|
|
SaveConfiguration(ctx context.Context, config *iam_pb.S3ApiConfiguration) error
|
|
|
|
// CreateUser creates a new user with the given identity
|
|
CreateUser(ctx context.Context, identity *iam_pb.Identity) error
|
|
|
|
// GetUser retrieves a user by username
|
|
GetUser(ctx context.Context, username string) (*iam_pb.Identity, error)
|
|
|
|
// UpdateUser updates an existing user
|
|
UpdateUser(ctx context.Context, username string, identity *iam_pb.Identity) error
|
|
|
|
// DeleteUser removes a user by username
|
|
DeleteUser(ctx context.Context, username string) error
|
|
|
|
// ListUsers returns all usernames
|
|
ListUsers(ctx context.Context) ([]string, error)
|
|
|
|
// GetUserByAccessKey retrieves a user by access key
|
|
GetUserByAccessKey(ctx context.Context, accessKey string) (*iam_pb.Identity, error)
|
|
|
|
// CreateAccessKey creates a new access key for a user
|
|
CreateAccessKey(ctx context.Context, username string, credential *iam_pb.Credential) error
|
|
|
|
// DeleteAccessKey removes an access key for a user
|
|
DeleteAccessKey(ctx context.Context, username string, accessKey string) error
|
|
|
|
// Policy Management
|
|
GetPolicies(ctx context.Context) (map[string]policy_engine.PolicyDocument, error)
|
|
// ListPolicyNames returns the names of all policies
|
|
ListPolicyNames(ctx context.Context) ([]string, error)
|
|
// PutPolicy creates or replaces a policy document.
|
|
PutPolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error
|
|
DeletePolicy(ctx context.Context, name string) error
|
|
GetPolicy(ctx context.Context, name string) (*policy_engine.PolicyDocument, error)
|
|
|
|
// Service Account Management
|
|
CreateServiceAccount(ctx context.Context, sa *iam_pb.ServiceAccount) error
|
|
UpdateServiceAccount(ctx context.Context, id string, sa *iam_pb.ServiceAccount) error
|
|
DeleteServiceAccount(ctx context.Context, id string) error
|
|
GetServiceAccount(ctx context.Context, id string) (*iam_pb.ServiceAccount, error)
|
|
ListServiceAccounts(ctx context.Context) ([]*iam_pb.ServiceAccount, error)
|
|
GetServiceAccountByAccessKey(ctx context.Context, accessKey string) (*iam_pb.ServiceAccount, error)
|
|
|
|
// User Policy Attachment Management
|
|
// AttachUserPolicy attaches a managed policy to a user by policy name
|
|
AttachUserPolicy(ctx context.Context, username string, policyName string) error
|
|
// DetachUserPolicy detaches a managed policy from a user
|
|
DetachUserPolicy(ctx context.Context, username string, policyName string) error
|
|
// ListAttachedUserPolicies returns the list of policy names attached to a user
|
|
ListAttachedUserPolicies(ctx context.Context, username string) ([]string, error)
|
|
|
|
// Group Management
|
|
CreateGroup(ctx context.Context, group *iam_pb.Group) error
|
|
GetGroup(ctx context.Context, groupName string) (*iam_pb.Group, error)
|
|
DeleteGroup(ctx context.Context, groupName string) error
|
|
ListGroups(ctx context.Context) ([]string, error)
|
|
UpdateGroup(ctx context.Context, group *iam_pb.Group) error
|
|
|
|
// Shutdown performs cleanup when the store is being shut down
|
|
Shutdown()
|
|
}
|
|
|
|
// AccessKeyInfo represents access key information with metadata
|
|
type AccessKeyInfo struct {
|
|
AccessKey string `json:"accessKey"`
|
|
SecretKey string `json:"secretKey"`
|
|
Username string `json:"username"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
// UserCredentials represents a user's credentials and metadata
|
|
type UserCredentials struct {
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
Account *iam_pb.Account `json:"account,omitempty"`
|
|
Credentials []*iam_pb.Credential `json:"credentials"`
|
|
Actions []string `json:"actions"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
UpdatedAt time.Time `json:"updatedAt"`
|
|
}
|
|
|
|
// PolicyManager interface for managing IAM policies
|
|
type PolicyManager interface {
|
|
GetPolicies(ctx context.Context) (map[string]policy_engine.PolicyDocument, error)
|
|
CreatePolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error
|
|
UpdatePolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error
|
|
DeletePolicy(ctx context.Context, name string) error
|
|
GetPolicy(ctx context.Context, name string) (*policy_engine.PolicyDocument, error)
|
|
}
|
|
|
|
// InlinePolicyStore is an optional interface for credential stores that support
|
|
// per-user inline policy storage. Stores that implement this interface preserve
|
|
// the exact policy document submitted via PutUserPolicy, enabling lossless
|
|
// round-trips through GetUserPolicy.
|
|
type InlinePolicyStore interface {
|
|
PutUserInlinePolicy(ctx context.Context, userName, policyName string, document policy_engine.PolicyDocument) error
|
|
GetUserInlinePolicy(ctx context.Context, userName, policyName string) (*policy_engine.PolicyDocument, error)
|
|
DeleteUserInlinePolicy(ctx context.Context, userName, policyName string) error
|
|
ListUserInlinePolicies(ctx context.Context, userName string) ([]string, error)
|
|
}
|
|
|
|
// GroupInlinePolicyStore is an optional interface for credential stores that
|
|
// support per-group inline policy storage. Mirrors InlinePolicyStore so the
|
|
// IAM API can persist group-attached inline policies without writing a
|
|
// shared filer file.
|
|
type GroupInlinePolicyStore interface {
|
|
PutGroupInlinePolicy(ctx context.Context, groupName, policyName string, document policy_engine.PolicyDocument) error
|
|
GetGroupInlinePolicy(ctx context.Context, groupName, policyName string) (*policy_engine.PolicyDocument, error)
|
|
DeleteGroupInlinePolicy(ctx context.Context, groupName, policyName string) error
|
|
ListGroupInlinePolicies(ctx context.Context, groupName string) ([]string, error)
|
|
}
|
|
|
|
// InlinePoliciesLoader is an optional interface for credential stores that can
|
|
// bulk-load every user inline policy in a single round trip.
|
|
type InlinePoliciesLoader interface {
|
|
LoadInlinePolicies(ctx context.Context) (map[string]map[string]policy_engine.PolicyDocument, error)
|
|
}
|
|
|
|
// GroupInlinePoliciesLoader is an optional interface for credential stores
|
|
// that can bulk-load every group inline policy in a single round trip.
|
|
type GroupInlinePoliciesLoader interface {
|
|
LoadGroupInlinePolicies(ctx context.Context) (map[string]map[string]policy_engine.PolicyDocument, error)
|
|
}
|
|
|
|
// UserRenamer is an optional interface for credential stores that can
|
|
// atomically rename a user along with all rows that reference the old
|
|
// username (credentials, inline policies, etc). Backends with referential
|
|
// integrity (e.g. PostgreSQL with ON DELETE CASCADE foreign keys on
|
|
// user_inline_policies) MUST implement this so the IAM rename path can
|
|
// move the dependents within a single transaction; copying them via
|
|
// per-row Put / Get / Delete calls would violate the FK at statement
|
|
// time because the new user row does not yet exist.
|
|
type UserRenamer interface {
|
|
RenameUser(ctx context.Context, oldName, newName string) error
|
|
}
|
|
|
|
// Stores holds all available credential store implementations
|
|
var Stores []CredentialStore
|