/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file 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 Amazon.Runtime; using Amazon.Runtime.Internal; using Amazon.Runtime.Internal.Auth; using Amazon.Runtime.Internal.Transform; using Amazon.Runtime.Internal.Util; using System; using System.Globalization; namespace Amazon.RDS.Util { /// /// Provides authorization tokens for IAM authentication to an RDS database. /// public static class RDSAuthTokenGenerator { private const string RDSServiceName = "rds-db"; private const string HTTPGet = "GET"; private const string HTTPS = "https"; private const string URISchemeDelimiter = "://"; private const string ActionKey = "Action"; private const string ActionValue = "connect"; private const string DBUserKey = "DBUser"; private const string XAmzExpires = "X-Amz-Expires"; private const string XAmzSecurityToken = "X-Amz-Security-Token"; private static readonly TimeSpan FifteenMinutes = TimeSpan.FromMinutes(15); /// /// AWS4PreSignedUrlSigner is built around operation request objects. /// This request type will only be used to generate the signed token. /// It will never be used to make an actual request to RDS. /// private class GenerateRDSAuthTokenRequest : AmazonWebServiceRequest { public GenerateRDSAuthTokenRequest() { ((IAmazonWebServiceRequest)this).SignatureVersion = SignatureVersion.SigV4; } } /// /// Generate a token for IAM authentication to an RDS database. /// /// Token generation additionally requires a RegionEndpoint and AWSCredentials. /// They will be loaded from the application's default configuration, /// and if unsuccessful from the Instance Profile service on an EC2 instance. /// /// /// Hostname of the RDS database. /// Port of the RDS database. /// Database user for the token. /// public static string GenerateAuthToken(string hostname, int port, string dbUser) { RegionEndpoint region = FallbackRegionFactory.GetRegionEndpoint(); return GenerateAuthToken(region, hostname, port, dbUser); } /// /// Generate a token for IAM authentication to an RDS database. /// /// Token generation additionally requires AWSCredentials. /// They will be loaded from the application's default configuration, /// and if unsuccessful from the Instance Profile service on an EC2 instance. /// /// /// The region of the RDS database. /// Hostname of the RDS database. /// Port of the RDS database. /// Database user for the token. /// public static string GenerateAuthToken(RegionEndpoint region, string hostname, int port, string dbUser) { AWSCredentials credentials = FallbackCredentialsFactory.GetCredentials(); return GenerateAuthToken(credentials, region, hostname, port, dbUser); } /// /// Generate a token for IAM authentication to an RDS database. /// /// Token generation additionally requires a RegionEndpoint. /// It will be loaded from the application's default configuration, /// and if unsuccessful from the Instance Profile service on an EC2 instance. /// /// /// The credentials for the token. /// Hostname of the RDS database. /// Port of the RDS database. /// Database user for the token. /// public static string GenerateAuthToken(AWSCredentials credentials, string hostname, int port, string dbUser) { RegionEndpoint region = FallbackRegionFactory.GetRegionEndpoint(); return GenerateAuthToken(credentials, region, hostname, port, dbUser); } /// /// Generate a token for IAM authentication to an RDS database. /// /// The credentials for the token. /// The region of the RDS database. /// Hostname of the RDS database. /// Port of the RDS database. /// Database user for the token. /// public static string GenerateAuthToken(AWSCredentials credentials, RegionEndpoint region, string hostname, int port, string dbUser) { if (credentials == null) throw new ArgumentNullException("credentials"); var immutableCredentials = credentials.GetCredentials(); return GenerateAuthToken(immutableCredentials, region, hostname, port, dbUser); } #if AWS_ASYNC_API /// /// Generate a token for IAM authentication to an RDS database. /// /// The credentials for the token. /// The region of the RDS database. /// Hostname of the RDS database. /// Port of the RDS database. /// Database user for the token. /// public static async System.Threading.Tasks.Task GenerateAuthTokenAsync(AWSCredentials credentials, RegionEndpoint region, string hostname, int port, string dbUser) { if (credentials == null) throw new ArgumentNullException("credentials"); var immutableCredentials = await credentials.GetCredentialsAsync().ConfigureAwait(false); return GenerateAuthToken(immutableCredentials, region, hostname, port, dbUser); } #endif private static string GenerateAuthToken(ImmutableCredentials immutableCredentials, RegionEndpoint region, string hostname, int port, string dbUser) { if (immutableCredentials == null) throw new ArgumentNullException("immutableCredentials"); if (region == null) throw new ArgumentNullException("region"); if (port < 0 || port > 65535) throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "{0} is an invalid port. Port must be 0 to 65535.", port)); hostname = hostname?.Trim(); if (string.IsNullOrEmpty(hostname)) throw new ArgumentException("Hostname must not be null or empty."); dbUser = dbUser?.Trim(); if (string.IsNullOrEmpty(dbUser)) throw new ArgumentException("DBUser must not be null or empty."); GenerateRDSAuthTokenRequest authTokenRequest = new GenerateRDSAuthTokenRequest(); IRequest request = new DefaultRequest(authTokenRequest, RDSServiceName); request.UseQueryString = true; request.HttpMethod = HTTPGet; request.Parameters.Add(XAmzExpires, FifteenMinutes.TotalSeconds.ToString(CultureInfo.InvariantCulture)); request.Parameters.Add(DBUserKey, dbUser); request.Parameters.Add(ActionKey, ActionValue); request.Endpoint = new UriBuilder(HTTPS, hostname, port).Uri; if (immutableCredentials.UseToken) { request.Parameters[XAmzSecurityToken] = immutableCredentials.Token; } var signingResult = AWS4PreSignedUrlSigner.SignRequest(request, null, new RequestMetrics(), immutableCredentials.AccessKey, immutableCredentials.SecretKey, RDSServiceName, region.SystemName); var authorization = "&" + signingResult.ForQueryParameters; var url = AmazonServiceClient.ComposeUrl(request); // remove the https:// and append the authorization return url.AbsoluteUri.Substring(HTTPS.Length + URISchemeDelimiter.Length) + authorization; } } }