You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
8.0 KiB
274 lines
8.0 KiB
#!/usr/bin/env python3
|
|
# /// script
|
|
# requires-python = ">=3.12"
|
|
# dependencies = [
|
|
# "boto3",
|
|
# ]
|
|
# ///
|
|
|
|
import argparse
|
|
import json
|
|
import random
|
|
import string
|
|
import subprocess
|
|
from enum import Enum
|
|
from pathlib import Path
|
|
|
|
import boto3
|
|
|
|
REGION_NAME = "us-east-1"
|
|
|
|
|
|
class Actions(str, Enum):
|
|
Get = "Get"
|
|
Put = "Put"
|
|
List = "List"
|
|
|
|
|
|
def get_user_dir(bucket_name, user, with_bucket=True):
|
|
if with_bucket:
|
|
return f"{bucket_name}/user-id-{user}"
|
|
|
|
return f"user-id-{user}"
|
|
|
|
|
|
def create_power_user():
|
|
power_user_key = "power_user_key"
|
|
power_user_secret = "power_user_secret"
|
|
command = f"s3.configure -apply -user poweruser -access_key {power_user_key} -secret_key {power_user_secret} -actions Admin"
|
|
print("Creating Power User...")
|
|
subprocess.run(
|
|
["docker", "exec", "-i", "seaweedfs-master-1", "weed", "shell"],
|
|
input=command,
|
|
text=True,
|
|
stdout=subprocess.PIPE,
|
|
)
|
|
print(
|
|
f"Power User created with key: {power_user_key} and secret: {power_user_secret}"
|
|
)
|
|
return power_user_key, power_user_secret
|
|
|
|
|
|
def create_bucket(s3_client, bucket_name):
|
|
print(f"Creating Bucket {bucket_name}...")
|
|
s3_client.create_bucket(Bucket=bucket_name)
|
|
print(f"Bucket {bucket_name} created.")
|
|
|
|
|
|
def upload_file(s3_client, bucket_name, user, file_path, custom_remote_path=None):
|
|
user_dir = get_user_dir(bucket_name, user, with_bucket=False)
|
|
if custom_remote_path:
|
|
remote_path = custom_remote_path
|
|
else:
|
|
remote_path = f"{user_dir}/{str(Path(file_path).name)}"
|
|
|
|
print(f"Uploading {file_path} for {user}... on {user_dir}")
|
|
|
|
s3_client.upload_file(file_path, bucket_name, remote_path)
|
|
print(f"File {file_path} uploaded for {user}.")
|
|
|
|
|
|
def create_user(iam_client, user):
|
|
print(f"Creating user {user}...")
|
|
response = iam_client.create_access_key(UserName=user)
|
|
print(
|
|
f"User {user} created with access key: {response['AccessKey']['AccessKeyId']}"
|
|
)
|
|
return response
|
|
|
|
|
|
def list_files(s3_client, bucket_name, path=None):
|
|
if path is None:
|
|
path = ""
|
|
print(f"Listing files of s3://{bucket_name}/{path}...")
|
|
try:
|
|
response = s3_client.list_objects_v2(Bucket=bucket_name, Prefix=path)
|
|
if "Contents" in response:
|
|
for obj in response["Contents"]:
|
|
print(f"\t - {obj['Key']}")
|
|
else:
|
|
print("No files found.")
|
|
except Exception as e:
|
|
print(f"Error listing files: {e}")
|
|
|
|
|
|
def create_policy_for_user(
|
|
iam_client, user, bucket_name, actions=[Actions.Get, Actions.List]
|
|
):
|
|
print(f"Creating policy for {user} on {bucket_name}...")
|
|
policy_document = {
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": [f"s3:{action.value}*" for action in actions],
|
|
"Resource": [
|
|
f"arn:aws:s3:::{get_user_dir(bucket_name, user)}/*",
|
|
],
|
|
}
|
|
],
|
|
}
|
|
policy_name = f"{user}-{bucket_name}-full-access"
|
|
|
|
policy_json = json.dumps(policy_document)
|
|
filepath = f"/tmp/{policy_name}.json"
|
|
with open(filepath, "w") as f:
|
|
f.write(json.dumps(policy_document, indent=2))
|
|
|
|
iam_client.put_user_policy(
|
|
PolicyName=policy_name, PolicyDocument=policy_json, UserName=user
|
|
)
|
|
print(f"Policy for {user} on {bucket_name} created.")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="SeaweedFS S3 Test Script")
|
|
parser.add_argument(
|
|
"--s3-url", default="http://127.0.0.1:8333", help="S3 endpoint URL"
|
|
)
|
|
parser.add_argument(
|
|
"--iam-url", default="http://127.0.0.1:8111", help="IAM endpoint URL"
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
bucket_name = (
|
|
f"test-bucket-{''.join(random.choices(string.digits + 'abcdef', k=8))}"
|
|
)
|
|
sentinel_file = "/tmp/SENTINEL"
|
|
with open(sentinel_file, "w") as f:
|
|
f.write("Hello World")
|
|
print(f"SENTINEL file created at {sentinel_file}")
|
|
|
|
power_user_key, power_user_secret = create_power_user()
|
|
|
|
admin_s3_client = get_s3_client(args, power_user_key, power_user_secret)
|
|
iam_client = get_iam_client(args, power_user_key, power_user_secret)
|
|
|
|
create_bucket(admin_s3_client, bucket_name)
|
|
upload_file(admin_s3_client, bucket_name, "Alice", sentinel_file)
|
|
upload_file(admin_s3_client, bucket_name, "Bob", sentinel_file)
|
|
list_files(admin_s3_client, bucket_name)
|
|
|
|
alice_user_info = create_user(iam_client, "Alice")
|
|
bob_user_info = create_user(iam_client, "Bob")
|
|
|
|
alice_key = alice_user_info["AccessKey"]["AccessKeyId"]
|
|
alice_secret = alice_user_info["AccessKey"]["SecretAccessKey"]
|
|
bob_key = bob_user_info["AccessKey"]["AccessKeyId"]
|
|
bob_secret = bob_user_info["AccessKey"]["SecretAccessKey"]
|
|
|
|
# Make sure Admin can read any files
|
|
list_files(admin_s3_client, bucket_name)
|
|
list_files(
|
|
admin_s3_client,
|
|
bucket_name,
|
|
get_user_dir(bucket_name, "Alice", with_bucket=False),
|
|
)
|
|
list_files(
|
|
admin_s3_client,
|
|
bucket_name,
|
|
get_user_dir(bucket_name, "Bob", with_bucket=False),
|
|
)
|
|
|
|
# Create read policy for Alice and Bob
|
|
create_policy_for_user(iam_client, "Alice", bucket_name)
|
|
create_policy_for_user(iam_client, "Bob", bucket_name)
|
|
|
|
alice_s3_client = get_s3_client(args, alice_key, alice_secret)
|
|
|
|
# Make sure Alice can read her files
|
|
list_files(
|
|
alice_s3_client,
|
|
bucket_name,
|
|
get_user_dir(bucket_name, "Alice", with_bucket=False) + "/",
|
|
)
|
|
|
|
# Make sure Bob can read his files
|
|
bob_s3_client = get_s3_client(args, bob_key, bob_secret)
|
|
list_files(
|
|
bob_s3_client,
|
|
bucket_name,
|
|
get_user_dir(bucket_name, "Bob", with_bucket=False) + "/",
|
|
)
|
|
|
|
# Update policy to include write
|
|
create_policy_for_user(iam_client, "Alice", bucket_name, actions=[Actions.Put, Actions.Get, Actions.List]) # fmt: off
|
|
create_policy_for_user(iam_client, "Bob", bucket_name, actions=[Actions.Put, Actions.Get, Actions.List]) # fmt: off
|
|
|
|
print("############################# Make sure Alice can write her files")
|
|
upload_file(
|
|
alice_s3_client,
|
|
bucket_name,
|
|
"Alice",
|
|
sentinel_file,
|
|
custom_remote_path=f"{get_user_dir(bucket_name, 'Alice', with_bucket=False)}/SENTINEL_by_Alice",
|
|
)
|
|
|
|
|
|
print("############################# Make sure Bob can write his files")
|
|
upload_file(
|
|
bob_s3_client,
|
|
bucket_name,
|
|
"Bob",
|
|
sentinel_file,
|
|
custom_remote_path=f"{get_user_dir(bucket_name, 'Bob', with_bucket=False)}/SENTINEL_by_Bob",
|
|
)
|
|
|
|
|
|
print("############################# Make sure Alice can read her new files")
|
|
list_files(
|
|
alice_s3_client,
|
|
bucket_name,
|
|
get_user_dir(bucket_name, "Alice", with_bucket=False) + "/",
|
|
)
|
|
|
|
|
|
print("############################# Make sure Bob can read his new files")
|
|
list_files(
|
|
bob_s3_client,
|
|
bucket_name,
|
|
get_user_dir(bucket_name, "Bob", with_bucket=False) + "/",
|
|
)
|
|
|
|
|
|
print("############################# Make sure Bob cannot read Alice's files")
|
|
list_files(
|
|
bob_s3_client,
|
|
bucket_name,
|
|
get_user_dir(bucket_name, "Alice", with_bucket=False) + "/",
|
|
)
|
|
|
|
print("############################# Make sure Alice cannot read Bob's files")
|
|
|
|
list_files(
|
|
alice_s3_client,
|
|
bucket_name,
|
|
get_user_dir(bucket_name, "Bob", with_bucket=False) + "/",
|
|
)
|
|
|
|
|
|
|
|
def get_iam_client(args, access_key, secret_key):
|
|
iam_client = boto3.client(
|
|
"iam",
|
|
endpoint_url=args.iam_url,
|
|
region_name=REGION_NAME,
|
|
aws_access_key_id=access_key,
|
|
aws_secret_access_key=secret_key,
|
|
)
|
|
return iam_client
|
|
|
|
|
|
def get_s3_client(args, access_key, secret_key):
|
|
s3_client = boto3.client(
|
|
"s3",
|
|
endpoint_url=args.s3_url,
|
|
region_name=REGION_NAME,
|
|
aws_access_key_id=access_key,
|
|
aws_secret_access_key=secret_key,
|
|
)
|
|
return s3_client
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|