I am writing a very simple custom attribute to be used with my methods for ASP.net Core. The attribute is to handle feature flags which indicate an endpoint method is "switched on or off" as follows:
1) If a feature is turned ON, allow the code to pass through to the method and execute it as normal.
2) If the feature is turned OFF, just return from the attribute and dont execute the method within
I was thinking something along the lines of this:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class FeatureFlagAttribute : ActionFilterAttribute
{
private Dictionary<string, bool> myFeaturesList;
private readonly string selectedFeature;
public FeatureFlagAttribute(string featureName)
{
selectedFeature = featureName;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var found = myFeaturesList.TryGetValue(selectedFeature, out var result);
if (!found || !result)
{
// dont continue
context.HttpContext.Response.StatusCode = 403;
}
}
}
I need the myFeaturesList populated for this to work BUT I dont want to pass it into the constructor every time this is being used. Whats the best way to configure this? I was thinking of setting a static property in the attribute but thought this was a bit of a lame approach and that there must be a better way. Thanks in advance!
Alternatively, you could extract the creation of featureNames into an injectable service (registered to DI) and use your attribute as a type filter or with IFilterFactory.
Using type filters, you would create your attribute as:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class FeatureFlagAttribute : ActionFilterAttribute
{
private readonly string _featureName;
private readonly IFeatureService _featureService;
public FeatureFlagAttribute(string featureName, IFeatureService featureService)
{
_featureName = featureName;
_featureService = featureService;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var features = _featureService.GetMyFeatures();
var found = features.TryGetValue(_featureName, out var result);
if (!found || !result)
{
// don't continue
context.HttpContext.Response.StatusCode = 403;
}
}
}
In the constructor parameters, featureName stays the same, and needs to be defined to the attribute, while featureService will get resolved from the DI, so you need to register an implementation for this in your startup's ConfigureServices().
The attribute usage changes a bit with type filters, for example:
[TypeFilter(typeof(FeatureFlagAttribute), Arguments = new object[] { "feature-A" })]
public IActionResult Index()
{
return View();
}
You can read more options of injecting dependencies into filters in the docs:
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
A different approach, but maybe move that out of the attribute, perhaps using a static event as the API hook? then you can put the dictionary where-ever you want?
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class FeatureFlagAttribute : ActionFilterAttribute
{
public FeatureFlagAttribute(string featureName)
{
selectedFeature = featureName;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (IsActive?.Invoke(selectedFeature) == false)
{
// dont continue
context.HttpContext.Response.StatusCode = 403;
}
}
public static event Func<string, bool> IsActive;
}
(note that you need to be careful with static events not to cause memory leaks)
Alternatively, keep what you have, but make the dictionary static (and thread-protected, etc); then add some kind of API like:
public static void SetFeatureEnabled(string featureName, bool enabled);
that tweaks the static dictionary.
Related
I have a asp dotnetcore web service that exposes some endpoints. For some of the endpoints, I want to run a check if existing scorecard is visible. The endpoints urls are:
GET /api/v1/scorecard/{ScorecardId}/details
GET /api/v1/scorecard/{ScorecardId}/rankings
These endpoints are just examples but they could be tens in numbers. Each of these endpoints have their own handlers like:
public async Task<ScorecardDetails> Get(long scorecardId)
{}
public async Task<ScorecardRankings> Get(long scorecardId)
{}
In database, there is a table Scorecard that stores the scorecard details and has a column IsVisible. I want to return 404 for all calls to these scorecard endpoints for scorecards that are set IsVisible = False in database.
I think you should consider using IActionFilter and IAsyncActionFilter for this purpose. In there you have a chance to read the already bound-model for parameters to better validate it. Of course that way has its own complexity unless you accept the way in which we decorate every parameters on every methods that require to check the existence of objects. That way is fairly inconvenient but to make it convenient, you need to design a model to allow you to declare (setup or configure) your targeted endpoints as well as how to target the required parameters for the existence checking process.
Here I introduce the way of using a middleware, just like what you want originally. It sounds more convenient than using action filters but it has its own complexity and inconvenience. At the phase of the middleware, we don't have any data bound to parameters and even not any RouteData available yet. That means we need to parse for the route values (here only the object's id) from the path. Parsing is a complex job especially when we need to make it fast. However I think using Regex for this purpose here is acceptable (although the framework code does not seem to like using Regex for the best performance). The framework code has a much more strict requirement for performance because it's the platform we build everything on. But in your code, you can take tradeoff between performance and easy-to-implement.
First we need a custom middleware like this:
public class EnsureExistenceByIdFromRouteValuesMiddleware
{
readonly RequestDelegate _next;
readonly EnsureExistenceByIdFromRouteValuesOptions _options;
public EnsureExistenceByIdFromRouteValuesMiddleware(RequestDelegate next,
EnsureExistenceByIdFromRouteValuesOptions options)
{
_next = next;
_options = options;
}
public async Task Invoke(HttpContext context)
{
var serviceType = _options.ExistenceCheckingServiceType;
var routePatterns = _options.RoutePatterns;
if (serviceType != null && routePatterns != null && routePatterns.Count > 0)
{
var service = context.RequestServices.GetRequiredService(_options.ExistenceCheckingServiceType) as IExistenceCheckingService;
if (service != null)
{
var matchedRoute = routePatterns.Select(e => Regex.Match(context.Request.Path,
e ?? "",
RegexOptions.Compiled | RegexOptions.IgnoreCase,
TimeSpan.FromSeconds(3)))
.FirstOrDefault(e => e.Success);
var id = matchedRoute?.Groups?.Skip(1)?.FirstOrDefault()?.Value;
if (!string.IsNullOrEmpty(id))
{
var isExisted = await service.ExistsAsync(id);
if (!isExisted && !context.Response.HasStarted)
{
context.Response.StatusCode = 404;
if (!_options.LetMvcHandle404)
{
return;
}
}
}
}
}
await _next(context);
}
}
The associated options class:
public class EnsureExistenceByIdFromRouteValuesOptions
{
public IList<string> RoutePatterns { get; } = new List<string>();
public Type ExistenceCheckingServiceType { get; set; }
public bool LetMvcHandle404 { get; set; }
public EnsureExistenceByIdFromRouteValuesOptions AddRoutePattern(string pattern)
{
RoutePatterns.Add(pattern);
return this;
}
public EnsureExistenceByIdFromRouteValuesOptions ClearRoutePatterns()
{
RoutePatterns.Clear();
return this;
}
}
Your services (for checking object existence) should implement a common & well-known interface (used by the middleware) like this:
public interface IExistenceCheckingService
{
Task<bool> ExistsAsync(object id);
}
//this is a sample implementation (just for demo)
public class ExistenceCheckingService : IExistenceCheckingService
{
public Task<bool> ExistsAsync(object id)
{
//dummy implementation for testing, only id of 1 is existed.
return Task.FromResult(Equals(id, "1"));
}
}
We create a convenient extension class for using in Startup.ConfigureServices and Startup.Configure:
public static class EnsureExistenceByIdFromRouteValuesExtensions
{
public static IServiceCollection EnsureExistenceByIdFromRouteValues(this IServiceCollection services)
{
//configure the MvcOptions to add the custom middleware
return services.Configure<MvcOptions>(o => {
o.Filters.Add(new EnsureExistenceByIdFromRouteValuesActionFilter());
}).AddScoped<IExistenceCheckingService, ExistenceCheckingService>();
}
public static IApplicationBuilder UseEnsureExistenceByIdFromRouteValuesMiddleware(this IApplicationBuilder app,
EnsureExistenceByIdFromRouteValuesOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
return app.UseMiddleware<EnsureExistenceByIdFromRouteValuesMiddleware>(options);
}
public static IApplicationBuilder UseEnsureExistenceByIdFromRouteValuesMiddleware(this IApplicationBuilder app,
Action<EnsureExistenceByIdFromRouteValuesOptions> configureOptions)
{
if (configureOptions == null) throw new ArgumentNullException(nameof(configureOptions));
var options = new EnsureExistenceByIdFromRouteValuesOptions();
configureOptions(options);
return app.UseEnsureExistenceByIdFromRouteValuesMiddleware(options);
}
//we use this filter for lately handling the 404 (set by the middleware)
class EnsureExistenceByIdFromRouteValuesActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context) {}
public void OnActionExecuting(ActionExecutingContext context)
{
if(context.HttpContext.Response.StatusCode == 404)
{
context.Result = new StatusCodeResult(404);
}
}
}
}
Use it in Startup.ConfigureServices:
services.EnsureExistenceByIdFromRouteValues();
Use it in Startup.Configure:
app.UseEnsureExistenceByIdFromRouteValuesMiddleware(o => {
//add your Regex patterns here
o.AddRoutePattern("/scorecard/(\\d+)/details/?$");
o.AddRoutePattern("/scorecard/(\\d+)/rankings/?$");
o.ExistenceCheckingServiceType = typeof(IExistenceCheckingService);
//setting this to true to not short-circuit right after the middleware
//the MVC middleware next will handle this (in the action filter)
//That way you will have a chance to use a custom view for 404
//(otherwise such as for Web APIs, we can let this be false as by default).
//o.LetMvcHandle404 = true;
});
NOTE: you need to know regex to use this. In the code above, I include just 2 sample regexes (matching your sample paths posted in your question). The regex patten must include one captured group for the object id (the (\\d+) in the sample patterns). That should be the first group (or should be the only group).
First you need to change the return type of the Get functions so that they can return a 404.
So:
public async Task<ScorecardDetails> Get(long scorecardId)
Becomes (pseudo-code):
public async Task<IActionResult> Get(long scorecardId) {
if(ScoreCardExists(scorecardId)) {
ScorecardDetails details = GetDetails(scorecardId);
return Ok(details);
}else{
return NotFound();
}
}
I have this typefilter that was recently created and it is located in a separate project.
public class RolesFilterAttribute : TypeFilterAttribute
{
public RolesFilterAttribute() : base(typeof(RolesFilterAttributeImpl))
{
}
public class RolesFilterAttributeImpl : IActionFilter
{
private readonly ValidateRoleClient validateRoleClient;
private string Role;
private string SecretKey;
public RolesFilterAttributeImpl(string Role, string SecretKey, ValidateRoleClient validateRoleClient)
{
this.validateRoleClient = validateRoleClient;
this.Role = Role;
this.SecretKey = SecretKey;
}
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.HttpContext.Request.Cookies["Token"] != null || context.HttpContext.Request.Cookies["RefreshToken"] != null)
{
TokenViewModel tvm = new TokenViewModel
{
Token = context.HttpContext.Request.Cookies["Token"],
RefreshToken = context.HttpContext.Request.Cookies["RefreshToken"]
};
ValidateRoleViewModel vrvm = new ValidateRoleViewModel
{
Role = Role,
SecretKey = SecretKey,
Token = tvm
};
validateRoleClient.ValidateRole(vrvm);
}
}
public void OnActionExecuting(ActionExecutingContext context)
{
throw new NotImplementedException();
}
}
}
This is how I declare it above my method:
[TypeFilter(typeof(RolesFilterAttribute), Arguments = new object[] { "role", "abc1234" })]
public IActionResult About()
{
return View();
}
I believe I have declared what I needed to in my Startup.cs
services.AddScoped<ValidateRoleClient>();
services.AddScoped<RolesFilterAttribute>();
However, when I start the app and navigate to the about page, this is what I encounter:
An unhandled exception occurred while processing the request.
InvalidOperationException: A suitable constructor for type
'App.Link.Filters.RolesFilterAttribute' could not be located. Ensure
the type is concrete and services are registered for all parameters of
a public constructor.
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.FindApplicableConstructor(Type
instanceType, Type[] argumentTypes, out ConstructorInfo
matchingConstructor, out Nullable[] parameterMap)
What else am I missing that I am not declaring?
[TypeFilter(typeof(RolesFilterAttribute), …]
This says that the filter type you want to create is a RolesFilterAttribute. Within the type filter, you are also passing two arguments "role" and "abc1234". So when the type filter will trigger the creation of RolesFilterAttribute it will look for a constructor that takes those two strings. But there is only a single constructor:
public RolesFilterAttribute()
: base(typeof(RolesFilterAttributeImpl))
{ }
So you have two parameters for a parameter-less constructor. That’s why you are getting the error.
Instead, what you want to do is have the [TypeFilter] attribute create an actual filter. So you need to pass the RolesFilterAttributeImpl type there:
[TypeFilter(typeof(RolesFilterAttributeImpl), Arguments = new object[] { "role", "abc1234" })]
At that point, your RolesFilterAttribute also becomes redundant, so you can just get rid of that and just define the RolesFilterAttributeImpl (which I would rename to just RolesFilter since it’s a filter, not an attribute or an attribute implementation).
Furthermore, since you are using [TypeFilter], you do not need to register your filter type with your dependency injection container. You only need to have the dependencies of your type registered, so only ValidateRoleClient in your case.
So your filter implementation would just look like this:
public class RolesFilter : IActionFilter
{
private readonly ValidateRoleClient validateRoleClient;
private readonly string role;
private readonly string secretKey;
public RolesFilter(string role, string secretKey, ValidateRoleClient validateRoleClient)
{
this.validateRoleClient = validateRoleClient;
this.role = role;
this.secretKey = secretKey;
}
public void OnActionExecuted(ActionExecutedContext context)
{
// …
}
public void OnActionExecuting(ActionExecutingContext context)
{ }
}
[TypeFilter] is btw. just one way to allow specifying filters using attributes. You can also create your own attribute which is then actually just a filter factory which is responsible of creating the filter instance at run-time. That’s what [TypeFilter] does for you by default.
See also my related answer on the topic of using dependencies in MVC filters.
AuthenticationRequiredAttribute Class
public class AuthenticationRequiredAttribute : ActionFilterAttribute
{
ILoginTokenKeyApi _loginTokenKeyApi;
IMemoryCache _memoryCache;
public AuthenticationRequiredAttribute(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
_loginTokenKeyApi = new LoginTokenKeyController(new UnitOfWork());
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var memory = _memoryCache.Get(Constants.KEYNAME_FOR_AUTHENTICATED_PAGES);
string requestedPath = filterContext.HttpContext.Request.Path;
string tokenKey = filterContext.HttpContext.Session.GetString("TokenKey")?.ToString();
bool? isLoggedIn = _loginTokenKeyApi.IsLoggedInByTokenKey(tokenKey).Data;
if (isLoggedIn == null ||
!((bool)isLoggedIn) ||
!Constants.AUTHENTICATED_PAGES_FOR_NORMAL_USERS.Contains(requestedPath))
{
filterContext.Result = new JsonResult(new { HttpStatusCode.Unauthorized });
}
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
HomeController
public class HomeController : Controller
{
IUserApi _userApi;
ILoginTokenKeyApi _loginTokenKey;
IMemoryCache _memoryCache;
public HomeController(IUserApi userApi, ILoginTokenKeyApi loginTokenKey, IMemoryCache memoryCache)
{
_loginTokenKey = loginTokenKey;
_userApi = userApi;
_memoryCache = memoryCache;
}
[AuthenticationRequired] // There is AN ERROR !!
public IActionResult Example()
{
return View();
}
}
ERROR :
Error CS7036 There is no argument given that corresponds to the
required formal parameter 'memoryCache' of
'AuthenticationRequiredAttribute.AuthenticationRequiredAttribute(IMemoryCache)' Project.Ground.WebUI
My problem is actually : I cant use dependency injection in attribute classes.
I want to use that attribute without any parameter. Is there any solution to solve it? I use dependency injection but it cant be used for attributes. How I can use it?
As per the documentation, you have a few options here:
If your filters have dependencies that you need to access from DI, there are several supported approaches. You can apply your filter to a class or action method using one of the following:
ServiceFilterAttribute
TypeFilterAttribute
IFilterFactory implemented on your attribute
ServiceFilter or TypeFilter attributes
If you just want to get this working quickly, you can just use one of the first two options to apply your filter to a controller or a controller action. When doing this, your filter does not need to be an attribute itself:
[TypeFilter(typeof(ExampleActionFilter))]
public IActionResult Example()
=> View();
The ExampleActionFilter can then just implement e.g. IAsyncActionFilter and you can directly depend on things using constructor injection:
public class ExampleActionFilter : IAsyncActionFilter
{
private readonly IMemoryCache _memoryCache;
public ExampleActionFilter(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{ … }
}
You can also use the [ServiceFilter] attribute instead to get the same effect but then you will also need to register your ExampleActionFilter with the dependency injection container in your Startup.
Filter factory
If you need more flexibility, you can implement your own filter factory. This allows you to write the factory code to create the actual filter instance yourself. A possible implementation for the above ExampleActionFilter could look like this:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ExampleActionFilterAttribute : Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
return serviceProvider.GetService<ExampleActionFilter>();
}
}
You can then use that [ExampleActionFilter] attribute to make the MVC framework create an instance of the ExampleActionFilter for you, using the DI container.
Note that this implementation is basically the same thing that ServiceFilterAttribute does. It’s just that implementing it yourself avoids having to use the ServiceFilterAttribute directly and allows you to have your own attribute.
Using service locator
Finally, there is another quick option that allows you to avoid constructor injection completely. This uses the service locator pattern to resolve services dynamically when your filter actually runs. So instead of injecting the dependency and using it directly, you retrieve it explicitly from the context:
public class ExampleActionFilter : ActionFilterAttribute
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var memoryCache = context.HttpContext.RequestServices.GetService<IMemoryCache>();
// …
}
}
Instead of resolving at construction, ActionExecutingContext.HttpContext.RequestServices should give you a reference to the request's service container at the time of the request.
So:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var svc = filterContext.HttpContext.RequestServices;
var memCache = svc.GetService<IMemoryCache>();
//..etc
For .Net Core 5, Below syntax worked for me.
IAppUserService _appUserService = (IAppUserService)context.HttpContext.RequestServices.GetService(typeof(IAppUserService));
As I am working on Asp.Net core Authorization part, I needed a new property in AuthorizeAttribute which I want to utilize as a extra permission value. So, I have extended the AuthorizeAttribute in my own custom Authorize attribute. See below:
public class RoleAuthorizeAttribute : Microsoft.AspNetCore.Authorization.AuthorizeAttribute
{
public string Permission { get; private set; }
public RoleAuthorizeAttribute(string policy, string permission) : base(policy)
{
this.Permission = permission;
}
}
Then, I've created an AuthorizationHandler to check for the requirement as below:
public class RolePermissionAccessRequirement : AuthorizationHandler<RolePermissionDb>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolePermissionDb requirement)
{
// check here..
context.Succeed(requirement);
return Task.FromResult(0);
}
}
All respective service collection mapping I have already done, just omitted here.
Now, I want my attribute to use like this on controller action level:
[RoleAuthorize("DefaultPolicy", "CustomPermission")]
public IActionResult List()
{
}
Would anybody suggest me how would I access the permission property value given on the top of Action method in the handler RolePermissionAccessRequirement ??
I want to perform some sort of access rule based on custom permission value given in the Authorize attribute on top of Action method.
Thanks in advance!
To parametrize a custom Authorize attribute, create an authorization filter implementing IAsyncAuthorizationFilter. Then wrap the filter in a TypeFilterAttribute-derived attribute. This attribute can accept parameters and pass it to the authorization filter's constructor.
Usage example:
[AuthorizePermission(Permission.Foo, Permission.Bar)]
public IActionResult Index()
{
return View();
}
Implementation:
public class AuthorizePermissionAttribute : TypeFilterAttribute
{
public AuthorizePermissionAttribute(params Permission[] permissions)
: base(typeof(PermissionFilter))
{
Arguments = new[] { new PermissionRequirement(permissions) };
Order = Int32.MinValue;
}
}
public class PermissionFilter : Attribute, IAsyncAuthorizationFilter
{
private readonly IAuthorizationService _authService;
private readonly PermissionRequirement _requirement;
public PermissionFilter(
IAuthorizationService authService,
PermissionRequirement requirement)
{
//you can inject dependencies via DI
_authService = authService;
//the requirement contains permissions you set in attribute above
//for example: Permission.Foo, Permission.Bar
_requirement = requirement;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
bool ok = await _authService.AuthorizeAsync(
context.HttpContext.User, null, _requirement);
if (!ok) context.Result = new ChallengeResult();
}
}
In addition, register a PermissionHandler in DI to handle PermissionRequirement with permission list:
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
Look at this this GitHub project for a complete example.
I'm looking for a way to program a custom authorization filter in ASP.NET 5 as the current implementation relies in Policies/Requirements which in turn rely solely in the use of Claims, thus on the umpteenth and ever-changing Identity System of which I'm really tired of (I've tried all it's flavors).
I have a large set of permissions (over 200) which I don't want to code as Claims as I have my own repository for them and a lot faster way to be check against it than comparing hundreds of strings (that is what claims are in the end).
I need to pass a parameter in each attribute that should be checked against my custom repository of permissions:
[Authorize(Requires = enumPermission.DeleteCustomer)]
I know this is not the most frequent scenario, but I think it isn't an edge case. I've tried implementing it in the way described by #leastprivilege on his magnificent post "The State of Security in ASP.NET 5 and MVC 6: Authorization", but I've hit the same walls as the author, who has even opened an issue on the ASP.NET 5 github repo, which has been closed in a not too much clarifying manner: link
Any idea of how to achieve this? Maybe using other kind of filter? In that case, how?
Following is an example of how you can achieve this scenario:
Let's assume you have a service called IPermissionStore which validates if a given user has the required permissions specified on the attribute.
public class MyCustomAuthorizationFilterAttribute : Attribute, IFilterFactory, IOrderedFilter
{
private readonly Permision[] _permissions;
public MyCustomAuthorizationFilterAttribute(params Permision[] permissions)
{
_permissions = permissions;
}
public int Order { get; set; }
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var store = serviceProvider.GetRequiredService<IPermissionStore>();
return new MyCustomAuthorizationFilter(store, _permissions)
{
Order = Order
};
}
}
public class MyCustomAuthorizationFilter : IAuthorizationFilter, IOrderedFilter
{
private readonly IPermissionStore _store;
private readonly Permision[] _permissions;
public int Order { get; set; }
public MyCustomAuthorizationFilter(IPermissionStore store, params Permision[] permissions)
{
_store = store;
_permissions = permissions;
}
public void OnAuthorization(AuthorizationContext context)
{
// Check if the action has an AllowAnonymous filter
if (!HasAllowAnonymous(context))
{
var user = context.HttpContext.User;
var userIsAnonymous =
user == null ||
user.Identity == null ||
!user.Identity.IsAuthenticated;
if (userIsAnonymous)
{
Fail(context);
}
else
{
// check the store for permissions for the current user
}
}
}
private bool HasAllowAnonymous(AuthorizationContext context)
{
return context.Filters.Any(item => item is Microsoft.AspNet.Authorization.IAllowAnonymous);
}
private void Fail(AuthorizationContext context)
{
context.Result = new HttpUnauthorizedResult();
}
}
// Your action
[HttpGet]
[MyCustomAuthorizationFilter(Permision.CreateCustomer)]
public IEnumerable<string> Get()
{
//blah
}