/* * MinIO .NET Library for Amazon S3 Compatible Cloud Storage, (C) 2017 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System.Globalization; using System.Text; namespace Minio.DataModel; public class PostPolicy { public IList> Conditions { get; } = new List>(); /// /// Get the populated dictionary of policy data. /// /// Dictionary of policy data public IDictionary FormData { get; } = new Dictionary(StringComparer.Ordinal); public DateTime Expiration { get; set; } public string Key { get; private set; } public string Bucket { get; private set; } /// /// Set expiration policy. /// /// Expiration time for the policy public void SetExpires(DateTime expiration) { // this.formData.Add("expiration", expiration.ToString()); Expiration = expiration; } /// /// Set key policy. /// /// Object name for the policy public void SetKey(string key) { if (string.IsNullOrEmpty(key)) throw new ArgumentException("Object key cannot be null or empty", nameof(key)); Conditions.Add(new List<(string, string, string)> { ("eq", "$key", key) }); // this.formData.Add("key", key); Key = key; } /// /// Set key prefix policy. /// /// Object name prefix for the policy public void SetKeyStartsWith(string keyStartsWith) { if (string.IsNullOrEmpty(keyStartsWith)) throw new ArgumentException("Object key prefix cannot be null or empty", nameof(keyStartsWith)); Conditions.Add(new List<(string, string, string)> { ("starts-with", "$key", keyStartsWith) }); // this.formData.Add("key", keyStartsWith); } /// /// Set bucket policy. /// /// Bucket name for the policy public void SetBucket(string bucket) { if (string.IsNullOrEmpty(bucket)) throw new ArgumentException("Bucket name cannot be null or empty", nameof(bucket)); Conditions.Add(new List<(string, string, string)> { ("eq", "$bucket", bucket) }); // this.formData.Add("bucket", bucket); Bucket = bucket; } /// /// Set cache control /// /// CacheControl for the policy public void SetCacheControl(string cacheControl) { if (string.IsNullOrEmpty(cacheControl)) throw new ArgumentException("Cache-Control argument cannot be null or empty", nameof(cacheControl)); Conditions.Add(new List<(string, string, string)> { ("eq", "$Cache-Control", cacheControl) }); // this.formData.Add("Cache-Control", cacheControl); } /// /// Set content type policy. /// /// ContentType for the policy public void SetContentType(string contentType) { if (string.IsNullOrEmpty(contentType)) throw new ArgumentException("Content-Type argument cannot be null or empty", nameof(contentType)); Conditions.Add(new List<(string, string, string)> { ("eq", "$Content-Type", contentType) }); // this.formData.Add("Content-Type", contentType); } /// /// Set content encoding /// /// ContentEncoding for the policy public void SetContentEncoding(string contentEncoding) { if (string.IsNullOrEmpty(contentEncoding)) throw new ArgumentException("Content-Encoding argument cannot be null or empty", nameof(contentEncoding)); Conditions.Add(new List<(string, string, string)> { ("eq", "$Content-Encoding", contentEncoding) }); // this.formData.Add("Content-Encoding", contentEncoding); } /// /// Set content length /// /// ContentLength for the policy public void SetContentLength(long contentLength) { if (contentLength <= 0) throw new ArgumentException("Negative Content length", nameof(contentLength)); Conditions.Add(new List<(string, string, string)> { ("content-length-range", contentLength.ToString(CultureInfo.InvariantCulture), contentLength.ToString(CultureInfo.InvariantCulture)) }); } /// /// Set content range /// /// ContentRange for the policy /// public void SetContentRange(long startRange, long endRange) { if (startRange < 0 || endRange < 0) throw new ArgumentOutOfRangeException(nameof(endRange), "Negative start or end range"); if (startRange > endRange) throw new ArgumentException("Start range is greater than end range", nameof(startRange)); Conditions.Add(new List<(string, string, string)> { ("content-length-range", startRange.ToString(CultureInfo.InvariantCulture), endRange.ToString(CultureInfo.InvariantCulture)) }); } /// /// Set session token /// /// set session token public void SetSessionToken(string sessionToken) { if (!string.IsNullOrEmpty(sessionToken)) Conditions.Add( new List<(string, string, string)> { ("eq", "$x-amz-security-token", sessionToken) }); // this.formData.Add("x-amz-security-token", sessionToken); } /// /// Set the success action status of the object for this policy based upload. /// /// Success action status public void SetSuccessStatusAction(string status) { if (string.IsNullOrEmpty(status)) throw new ArgumentException("Status is Empty", nameof(status)); Conditions.Add(new List<(string, string, string)> { ("eq", "$success_action_status", status) }); // this.formData.Add("success_action_status", status); } /// /// Set user specified metadata as a key/value couple. /// /// Key and Value to insert in the metadata /// public void SetUserMetadata(string key, string value) { if (string.IsNullOrEmpty(key)) throw new ArgumentException("Key is Empty", nameof(key)); if (string.IsNullOrEmpty(value)) throw new ArgumentException("Value is Empty", nameof(value)); var headerName = $"x-amz-meta-{key}"; Conditions.Add(new List<(string, string, string)> { ("eq", $"${headerName}", value) }); FormData.Add(headerName, value); } /// /// Set signature algorithm policy. /// /// Set signature algorithm used for the policy public void SetAlgorithm(string algorithm) { if (string.IsNullOrEmpty(algorithm)) throw new ArgumentException("Algorithm argument cannot be null or empty", nameof(algorithm)); Conditions.Add(new List<(string, string, string)> { ("eq", "$x-amz-algorithm", algorithm) }); // this.formData.Add("x-amz-algorithm", algorithm); } /// /// Set credential policy. /// /// Set credential string for the policy public void SetCredential(string credential) { if (string.IsNullOrEmpty(credential)) throw new ArgumentException("credential argument cannot be null or empty", nameof(credential)); Conditions.Add(new List<(string, string, string)> { ("eq", "$x-amz-credential", credential) }); // this.formData.Add("x-amz-credential", credential); } /// /// Set date policy. /// /// Set date for the policy public void SetDate(DateTime date) { var dateStr = date.ToUniversalTime().ToString("yyyyMMddTHHmmssZ", CultureInfo.InvariantCulture); Conditions.Add(new List<(string, string, string)> { ("eq", "$x-amz-date", dateStr) }); // this.formData.Add("x-amz-date", dateStr); } /// /// Serialize policy into JSON string. /// /// Serialized JSON policy private ReadOnlySpan MarshalJSON() { var policyList = new List(); foreach (var condition in Conditions) policyList.Add("[\"" + condition[0].Item1 + "\",\"" + condition[0].Item2 + "\",\"" + condition[0].Item3 + "\"]"); // expiration and policies will never be empty because of checks at PresignedPostPolicy() var sb = new StringBuilder(); _ = sb.Append('{'); _ = sb.Append("\"expiration\":\"") .Append(Expiration.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)) .Append('"') .Append(','); _ = sb.Append("\"conditions\":[").AppendJoin(",", policyList).Append(']'); _ = sb.Append('}'); return Encoding.UTF8.GetBytes(sb.ToString()); } /// /// Compute base64 encoded form of JSON policy. /// /// Base64 encoded string of JSON policy public string Base64() { var policyStrBytes = MarshalJSON(); #if NETSTANDARD return Convert.ToBase64String(policyStrBytes.ToArray()); #else return Convert.ToBase64String(policyStrBytes); #endif } /// /// Verify if bucket is set in policy. /// /// true if bucket is set public bool IsBucketSet() { if (FormData.TryGetValue("bucket", out var value)) if (!string.IsNullOrEmpty(value)) return true; return false; } /// /// Verify if key is set in policy. /// /// true if key is set public bool IsKeySet() { if (FormData.TryGetValue("key", out var value)) if (!string.IsNullOrEmpty(value)) return true; return false; } /// /// Verify if expiration is set in policy. /// /// true if expiration is set public bool IsExpirationSet() { return !string.IsNullOrEmpty(Expiration.ToString(CultureInfo.InvariantCulture)); } }