Skip to content

Instantly share code, notes, and snippets.

@sharwell
Created August 12, 2014 19:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sharwell/f79e5aa52c8ad7d32af1 to your computer and use it in GitHub Desktop.
Save sharwell/f79e5aa52c8ad7d32af1 to your computer and use it in GitHub Desktop.
SoftLayer Identity Provider Example (WIP)
namespace net.openstack.Providers.SoftLayer
{
using System;
using System.Linq;
using net.openstack.Core.Caching;
using net.openstack.Core.Domain;
using net.openstack.Core.Providers;
using net.openstack.Providers.Rackspace;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using HttpMethod = JSIStudios.SimpleRESTServices.Client.HttpMethod;
using IRestService = JSIStudios.SimpleRESTServices.Client.IRestService;
using JsonRestServices = JSIStudios.SimpleRESTServices.Client.Json.JsonRestServices;
/// <summary>
/// Provides an implementation of <see cref="IIdentityProvider"/> for operating with
/// SoftLayer's authentication service.
/// </summary>
/// <threadsafety/>
/// <preliminary/>
public class SoftLayerIdentityProvider : CloudIdentityProvider
{
private static string UserAccessTemplate =
"{\n" +
" \"token\": {\n" +
" \"expires\": \"{ExpirationDate}\",\n" +
" \"id\": \"{X-Auth-Token}\",\n" +
" \"tenant\": {\n" +
" \"id\": \"{TenantId}\",\n" +
" \"name\": \"{TenantId}\"\n" +
" }\n" +
" },\n" +
" \"user\": {\n" +
" \"id\": \"{UserId}\",\n" +
" \"name\": \"{UserName}\",\n" +
" \"roles\": [],\n" +
" \"RAX-AUTH:defaultRegion\": \"NoRegion\"\n" +
" },\n" +
" \"serviceCatalog\": [\n" +
" {\n" +
" \"name\": \"cloudFiles\",\n" +
" \"type\": \"object-store\",\n" +
" \"endpoints\": [\n" +
" {\n" +
" \"publicURL\": \"{X-Storage-Url}\",\n" +
" \"region\": \"NoRegion\"\n" +
" }\n" +
" ]\n" +
" }\n" +
" ]\n" +
"}\n";
/// <summary>
/// Initializes a new instance of the <see cref="SoftLayerIdentityProvider"/> class
/// with no default identity, the specified base URL, and the default REST service
/// implementation and token cache.
/// </summary>
/// <param name="urlBase">The base URL for the cloud instance.</param>
/// <exception cref="ArgumentNullException">If <paramref name="urlBase"/> is <see langword="null"/>.</exception>
public SoftLayerIdentityProvider(Uri urlBase)
: this(urlBase, null, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SoftLayerIdentityProvider"/> class
/// with the specified default identity and base URL, and the default REST service
/// implementation and token cache.
/// </summary>
/// <param name="urlBase">The base URL for the cloud instance.</param>
/// <param name="defaultIdentity">The default identity to use for calls that do not explicitly specify an identity. If this value is <see langword="null"/>, no default identity is available so all calls must specify an explicit identity.</param>
/// <exception cref="ArgumentNullException">If <paramref name="urlBase"/> is <see langword="null"/>.</exception>
public SoftLayerIdentityProvider(Uri urlBase, CloudIdentity defaultIdentity)
: this(urlBase, defaultIdentity, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SoftLayerIdentityProvider"/> class
/// with no default identity, and the specified base URL, REST service
/// implementation, and token cache.
/// </summary>
/// <param name="urlBase">The base URL for the cloud instance.</param>
/// <param name="restService">The implementation of <see cref="IRestService"/> to use for executing REST requests. If this value is <see langword="null"/>, the provider will use a new instance of <see cref="JsonRestServices"/>.</param>
/// <param name="tokenCache">The cache to use for caching user access tokens. If this value is <see langword="null"/>, the provider will use <see cref="UserAccessCache.Instance"/>.</param>
/// <exception cref="ArgumentNullException">If <paramref name="urlBase"/> is <see langword="null"/>.</exception>
public SoftLayerIdentityProvider(Uri urlBase, IRestService restService, ICache<UserAccess> tokenCache)
: this(urlBase, null, restService, tokenCache)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SoftLayerIdentityProvider"/> class
/// using the provided values.
/// </summary>
/// <param name="urlBase">The base URL for the cloud instance.</param>
/// <param name="defaultIdentity">The default identity to use for calls that do not explicitly specify an identity. If this value is <see langword="null"/>, no default identity is available so all calls must specify an explicit identity.</param>
/// <param name="restService">The implementation of <see cref="IRestService"/> to use for executing REST requests. If this value is <see langword="null"/>, the provider will use a new instance of <see cref="JsonRestServices"/>.</param>
/// <param name="tokenCache">The cache to use for caching user access tokens. If this value is <see langword="null"/>, the provider will use <see cref="UserAccessCache.Instance"/>.</param>
/// <exception cref="ArgumentNullException">If <paramref name="urlBase"/> is <see langword="null"/>.</exception>
public SoftLayerIdentityProvider(Uri urlBase, CloudIdentity defaultIdentity, IRestService restService, ICache<UserAccess> tokenCache)
: base(defaultIdentity, restService, tokenCache, urlBase)
{
if (urlBase == null)
throw new ArgumentNullException("urlBase");
}
private static string GenerateUserAccess(DateTimeOffset expirationDate, string authToken, string tenantId, string userId, string userName, string storageUrl)
{
return UserAccessTemplate
.Replace("{ExpirationDate}", JsonConvert.SerializeObject(expirationDate).Trim('"'))
.Replace("{X-Auth-Token}", authToken)
.Replace("{TenantId}", tenantId)
.Replace("{UserId}", userId)
.Replace("{UserName}", userName)
.Replace("{X-Storage-Url}", storageUrl);
}
/// <inheritdoc/>
public override UserAccess GetUserAccess(CloudIdentity identity, bool forceCacheRefresh = false)
{
Func<UserAccess> refreshCallback =
() =>
{
// TODO: prepare and the authentication request to obtain the placeholders
JObject requestBody = null;
var response = ExecuteRESTRequest<JObject>(identity, new Uri(UrlBase, "/auth/v1.0"), HttpMethod.POST, requestBody, isTokenRequest: true);
if (response == null || response.Data == null)
return null;
DateTimeOffset expirationDate = DateTimeOffset.Now;
string authToken = response.Headers.First(i => i.Key.Equals("X-Auth-Token", StringComparison.OrdinalIgnoreCase)).Value;
string tenantId = identity.Username;
string userId = identity.Username;
string userName = identity.Username;
string storageUrl = response.Headers.First(i => i.Key.Equals("X-Storage-Url", StringComparison.OrdinalIgnoreCase)).Value;
UserAccess access = JsonConvert.DeserializeObject<UserAccess>(GenerateUserAccess(expirationDate, authToken, tenantId, userId, userName, storageUrl));
if (access == null || access.Token == null)
return null;
return access;
};
string key = string.Format("{0}:{1}/{2}/{3}", UrlBase, identity.Username, identity.APIKey, identity.Password);
var userAccess = TokenCache.Get(key, refreshCallback, forceCacheRefresh);
return userAccess;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment