Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Plugins/AWSS3/StorageAdminService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ public class StorageAdminService : IStorageAdminService
{
public Task<Credentials> CreateUserAsync(string username, AccessPermissions permissions, string[] bucketNames) => throw new NotImplementedException();
public Task<Credentials> CreateUserAsync(string username, PolicyRequest[] policyRequests) => throw new NotImplementedException();
public Task RemoveUserAsync(string username) => throw new NotImplementedException();
}
}
31 changes: 30 additions & 1 deletion src/Plugins/MinIO/StorageAdminService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,20 @@ private static void ValidateConfiguration(StorageServiceConfiguration configurat
}
}

private string CreateUserCmd(string username, string secretKey) => $"admin user add {_serviceName} {username} {secretKey}";
private string CreateUserCmd(string username, string secretKey)
{
Guard.Against.NullOrWhiteSpace(username, nameof(username));
Guard.Against.NullOrWhiteSpace(secretKey, nameof(secretKey));

return $"admin user add {_serviceName} {username} {secretKey}";
}

public async Task<bool> SetPolicyAsync(IdentityType policyType, List<string> policies, string itemName)
{
Guard.Against.Null(policyType, nameof(policyType));
Guard.Against.Null(policies, nameof(policies));
Guard.Against.NullOrWhiteSpace(itemName, nameof(itemName));

var policiesStr = string.Join(',', policies);
var setPolicyCmd = $"admin policy set {_serviceName} {policiesStr} {policyType.ToString().ToLower()}={itemName}";
var result = await ExecuteAsync(setPolicyCmd).ConfigureAwait(false);
Expand All @@ -91,6 +101,8 @@ public async Task<bool> SetPolicyAsync(IdentityType policyType, List<string> pol

private async Task<List<string>> ExecuteAsync(string cmd)
{
Guard.Against.NullOrWhiteSpace(cmd, nameof(cmd));

if (cmd.StartsWith("mc"))
{
throw new InvalidOperationException($"Incorrect command \"{cmd}\"");
Expand All @@ -110,6 +122,8 @@ private async Task<List<string>> ExecuteAsync(string cmd)

private static async Task<(List<string> Output, List<string> Errors)> RunProcessAsync(Process process)
{
Guard.Against.Null(process, nameof(process));

var output = new List<string>();
var errors = new List<string>();
process.Start();
Expand All @@ -132,6 +146,8 @@ private async Task<List<string>> ExecuteAsync(string cmd)

private Process CreateProcess(string cmd)
{
Guard.Against.NullOrWhiteSpace(cmd, nameof(cmd));

ProcessStartInfo startinfo = new()
{
FileName = _executableLocation,
Expand Down Expand Up @@ -172,12 +188,16 @@ public async Task<bool> SetConnectionAsync()

public async Task<bool> UserAlreadyExistsAsync(string username)
{
Guard.Against.NullOrWhiteSpace(username, nameof(username));

var result = await ExecuteAsync(_get_users_cmd).ConfigureAwait(false);
return result.Any(r => r.Contains(username));
}

public async Task RemoveUserAsync(string username)
{
Guard.Against.NullOrWhiteSpace(username, nameof(username));

var result = await ExecuteAsync($"admin user remove {_serviceName} {username}").ConfigureAwait(false);

if (!result.Any(r => r.Contains($"Removed user `{username}` successfully.")))
Expand All @@ -189,6 +209,9 @@ public async Task RemoveUserAsync(string username)
[Obsolete("CreateUserAsync with bucketNames is deprecated, please use CreateUserAsync with an array of PolicyRequest instead.")]
public async Task<Credentials> CreateUserAsync(string username, AccessPermissions permissions, string[] bucketNames)
{
Guard.Against.NullOrWhiteSpace(username, nameof(username));
Guard.Against.Null(bucketNames, nameof(bucketNames));

var policyRequests = new List<PolicyRequest>();

for (var i = 0; i < bucketNames.Length; i++)
Expand All @@ -201,6 +224,9 @@ public async Task<Credentials> CreateUserAsync(string username, AccessPermission

public async Task<Credentials> CreateUserAsync(string username, PolicyRequest[] policyRequests)
{
Guard.Against.NullOrWhiteSpace(username, nameof(username));
Guard.Against.Null(policyRequests, nameof(policyRequests));

if (!await SetConnectionAsync())
{
throw new InvalidOperationException("Unable to set connection for more information, attempt mc alias set {_serviceName} http://{_endpoint} {_accessKey} {_secretKey}");
Expand Down Expand Up @@ -247,6 +273,9 @@ public async Task<Credentials> CreateUserAsync(string username, PolicyRequest[]
/// <exception cref="InvalidOperationException"></exception>
private async Task<string> CreatePolicyAsync(PolicyRequest[] policyRequests, string username)
{
Guard.Against.Null(policyRequests, nameof(policyRequests));
Guard.Against.NullOrWhiteSpace(username, nameof(username));

var policyFileName = await CreatePolicyFile(policyRequests, username).ConfigureAwait(false);
var result = await ExecuteAsync($"admin policy add {_serviceName} pol_{username} {policyFileName}").ConfigureAwait(false);
if (result.Any(r => r.Contains($"Added policy `pol_{username}` successfully.")) is false)
Expand Down
19 changes: 12 additions & 7 deletions src/S3Policy/PolicyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,19 +99,19 @@ public static Policy ToPolicy(PolicyRequest[] policyRequests)
Sid = "AllowUserToSeeBucketListInTheConsole",
Action = new string[] {"s3:ListAllMyBuckets", "s3:GetBucketLocation" },
Effect = "Allow",
Resource = policyRequests.Select(pr => pr.BucketName).ToArray(),
Resource = policyRequests.Select(pr => pr.BucketName).Distinct().ToArray(),
},
new Statement
{
Sid = "AllowRootAndHomeListingOfBucket",
Action = new string[] { "s3:ListBucket" },
Effect = "Allow",
Resource = policyRequests.Select(pr => pr.BucketName).ToArray(),
Resource = policyRequests.Select(pr => pr.BucketName).Distinct().ToArray(),
Condition = new Condition
{
StringEquals = new StringEquals
{
S3Prefix = pathList.ToArray(),
S3Prefix = pathList.Distinct().ToArray(),
S3Delimiter = new string[] { "/" }
}
}
Expand All @@ -121,13 +121,15 @@ public static Policy ToPolicy(PolicyRequest[] policyRequests)
Sid = "AllowListingOfUserFolder",
Action = new string[] { "s3:ListBucket" },
Effect = "Allow",
Resource = policyRequests.Select(pr => pr.BucketName).ToArray(),
Resource = policyRequests.Select(pr => pr.BucketName).Distinct().ToArray(),
Condition = new Condition
{
StringLike = new StringLike
{
S3Prefix = policyRequests.Select(pr => $"{pr.FolderName}/*")
.Union( policyRequests.Select(pr => $"{pr.FolderName}")).ToArray()
S3Prefix = policyRequests
.Select(pr => $"{pr.FolderName}/*")
.Union( policyRequests.Select(pr => $"{pr.FolderName}"))
.Distinct().ToArray()
}
}
},
Expand All @@ -136,7 +138,10 @@ public static Policy ToPolicy(PolicyRequest[] policyRequests)
Sid = "AllowAllS3ActionsInUserFolder",
Action = new string[] { "s3:*" },
Effect = "Allow",
Resource = policyRequests.Select(pr => $"{pr.BucketName}/{pr.FolderName}/*").ToArray(),
Resource = policyRequests
.Select(pr => System.IO.Path.Join(pr.BucketName, pr.FolderName, "*"))
.Distinct()
.ToArray(),
},
}
};
Expand Down
6 changes: 6 additions & 0 deletions src/Storage/API/IStorageAdminService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,11 @@ public interface IStorageAdminService
/// <param name="policyRequests">Contains the buckets and folders that the user needs access to</param>
/// <returns></returns>
Task<Credentials> CreateUserAsync(string username, PolicyRequest[] policyRequests);

/// <summary>
/// Removes a user account
/// </summary>
/// <param name="username">Username</param>
Task RemoveUserAsync(string username);
}
}