If a max limit is set for autoquery in startup:
Plugins.Add(new AutoQueryFeature { MaxLimit = 100 });
Is there anyway to override this on a specific service endpoint where I override autoquery?
I want this enforced for all public endpoints but on admin side I want to be able to get as much data as I like but when I inject AutoQuery object into service I can't see any way to override the limit on the query.
You would need to create a Custom AutoQuery Implementation where you can override the SQL Expression AutoQuery generates, e.g:
public class MyQueryServices : Service
{
public IAutoQueryDb AutoQuery { get; set; }
//Override with custom implementation
public object Any(FindMovies query)
{
var q = AutoQuery.CreateQuery(query, base.Request);
q.Take(CustomLimit);
return AutoQuery.Execute(query, q);
}
}
Alternatively you should also be able to override the limit with an AutoQuery Filter:
var autoQuery = new AutoQueryFeature()
.RegisterQueryFilter<FindMovies, Movie>((q, dto, req) => {
if (req.GetSession().HasRole(RoleNames.Admin, req.TryResolve<IAuthRepository>())
{
q.Take(CustomLimit);
}
});
Plugins.Add(autoQuery);
Related
Since roles don't contain permissions. I am a bit confused by the Roles and Permission in ServiceStack. It appears they are really the same thing? I want to implement a Group, that has roles, that has permissions. Based on the servicestack default implementation I don't think I can extend the provider and get the nested information.
How would i achieve this and still use the authentication attributes.
If i had an attribute
[RequiredPermission("CanAccessPerm")]
That is in Role:HasAccessRole That is in Group:HasAccessGroup
I would want to only use perms to determine access at the API level. Then Roles and Groups to determine who has perms. If Roles contained permissions then I could just extend the CredentialsAuthProvider TryAuthenticate and additionally look at a group table. Is there a way to do this and not rewrite the whole authentication?
Edit 12/12
I am using
container.Register(c =>
new OrmLiteAuthRepository(c.Resolve())
{
UseDistinctRoleTables = AppSettings.Get("UseDistinctRoleTables", true),
});
How do I get to the IManage roles? I see i can override the IAuthRepository.
I found this link. But its not a replacement for Auth
ServiceStack - roles and permissions
-_Edit 12/29 -- It is not calling the methods in MyOrmLiteAuthRepository. Do you know why?
AppHost.cs
container.Register<IAuthRepository>(c =>
new MyOrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>())
{
UseDistinctRoleTables = AppSettings.Get("UseDistinctRoleTables", true),
});
CustomAuthRepo
public class MyOrmLiteAuthRepository : OrmLiteAuthRepository
{
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory) : base(dbFactory) { }
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory, string namedConnnection = null)
: base(dbFactory, namedConnnection)
{
DbFactory = dbFactory;
NamedConnnection = namedConnnection;
}
public IDbConnectionFactory DbFactory { get; set; }
public string NamedConnnection { get; set; }
public override ICollection<string> GetPermissions(string userAuthId)
{
var permissions = base.GetPermissions(userAuthId);
using (var ss = HostContext.ResolveService<SecurityService>(new BasicRequest()))
{
permissions = ss.UserPermissions(Convert.ToInt32(userAuthId));
}
return permissions;
}
public override bool HasPermission(string userAuthId, string permission)
{
var hasPermission = base.HasPermission(userAuthId, permission);
using (var ss = HostContext.ResolveService<SecurityService>(new BasicRequest()))
{
hasPermission = ss.UserHasPermInRoleOrGroup(permission, Convert.ToInt32(userAuthId));
}
return hasPermission;
}
}
Roles/Permissions work similar in that a User can have multiple Roles and Permissions but they're logically different in that a Role defines the Role a person has like "Employee", "Manager", etc and Permission defines functionality they have access to like "CanSubmitPurchaseOrders", "CanRefundCustomers", etc.
ServiceStack doesn't support Roles having permissions themselves but you can implement this functionality yourself in your own Custom AuthProvider by overriding OnAuthenticated() and populating the Permissions collections of AuthUserSession with a combination of all the permissions in all the Roles a User is in. If you're not using a custom AuthProvider you can modify the Users Session by implementing the OnAuthenticated() Session of Auth Event Hooks.
Alternatively if you're using an AuthRepository like OrmLiteAuthRepository you can change how permissions are managed by overriding its IManageRoles GetPermissions() and HasPermission() APIs to also inspect the Permissions that the Users Roles have assigned to them which you would need to maintain in an out-of-band table.
Overriding OrmLiteAuthRepository
OrmLiteAuthRepository implements IManageRoles so when needed you can cast IAuthRepository to IManageRoles, e.g:
var manageRoles = (IManageRoles)container.Resolve<IAuthRepository>();
You can override OrmLiteAuthRepository and implement your own GetPermissions() and HasPermission() with normal inheritance, e.g:
public class MyOrmLiteAuthRepository : OrmLiteAuthRepository
{
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory) : base(dbFactory) { }
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory, string namedConnnection = null)
: base(dbFactory, namedConnnection) {}
public override ICollection<string> GetPermissions(string userAuthId)
{
return base.GetPermissions(userAuthId);
}
public override bool HasPermission(string userAuthId, string permission)
{
return base.HasPermission(userAuthId, permission);
}
}
Unsure how IMobileServiceSyncTable security works - say I have a table, and it stores data for multiple users.
Following this Azure App Services tutorial, it looks like I can query - from a mobile app - for pretty much any record, for any user, that I want.
Client-side (e.g., Xamarin):
await todoTable.PullAsync("todoItems" + userid,
syncTable.Where(u => u.UserId = userid));
Is there a way (server-side) to automatically scope records to the current authenticated user? Or is that done for you automatically if you decorate your table controllers with the [Authorize] attribute?
Server-side:
[Authorize]
public class TodoItemController : TableController<TodoItem>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MyAppContext context = new MyAppContext();
DomainManager = new EntityDomainManager<TodoItem>(context, Request);
}
// GET tables/TodoItem
public IQueryable<TodoItem> GetAllTodoItems()
{
return Query();
}
// GET tables/TodoItem/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<TodoItem> GetTodoItem(string id)
{
return Lookup(id);
}
}
Check out this blog post (assuming ASP.NET): http://shellmonger.com/2016/05/09/30-days-of-zumo-v2-azure-mobile-apps-day-18-asp-net-authentication/ - it adjusts the table controller to do exactly what you want.
I'm trying to implement ServiceStack group deleting and updating. For group deleting, the endpoint is like ~/item/{ItemIdList}, the ItemIdList is of List<Guid> type. I already wrote the code but I'm not sure what to put into the {ItemIdList} part? For a single item, it is ~/item/AItem'sActualId. What if it is a list?
And is it the correct approach or how to implement group deleting/updating. Thanks.
It shouldn't be on the /path/info info since the url doesn't identify a resource, it's better to be specified on the queryString on the /items resource collection, e.g:
[Route("/items", "DELETE")]
public class DeleteItems
{
public List<Guid> Ids { get; set; }
}
Then call it with:
DELETE /items?Ids={Guid1},{Guid2}
or via a Service Client:
client.Delete(new DeleteItems { Ids = new[] { Guid1, Guid2 }.ToList());
If you want to be able to use ANY HTTP Verb, your custom path can be a command like:
[Route("/items/delete")]
public class DeleteItems
{
public List<Guid> Ids { get; set; }
}
Then handle it using Any in your Service:
public object Any(DeleteItems request) { ... }
Which you can now call with ANY HTTP Method, e.g:
client.Post(new DeleteItems { Ids = new[] { Guid1, Guid2 }.ToList());
In order to avoid getting the user data on every action I've create an custom action filter that gets the user by its ID and then passes to the action.
public class UserDataAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
...
// getting the user and storing it in the request properties
object user = userBLL.GetUserById(userId);
actionContext.Request.Properties.Add("User", user);
}
}
And the I can get the user object in the action method like this:
[Authorize]
[UserData]
[HttpGet]
[Route("dosomething")]
public IHttpActionResult DoSomething()
{
// retrieve the user
object user;
Request.Properties.TryGetValue("User", out user);
User u = (User)user;
return Ok();
}
However, in MVC it's possible to use ActionParameters in the filter to store something that will be used by the action method, like so:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
...
// Create object parameter.
filterContext.ActionParameters["User"] = userBLL.GetUserById(userId);
}
And then use the User object as if it were part of the original request:
[AddActionParameter]
public ActionResult Index(User user)
{
// Here I can access the user setted on the filter
...
return View();
}
So, my question is: There is a way in Web API 2 to pass the User object from the action filter to the action as an argument, just like in MVC?
With ASP.NET Web API, you can create a parameter binding to receive an object, User in your case. You don't have to create a filter for this. So, you will create a binding like this.
public class UserParameterBinding : HttpParameterBinding
{
public UserParameterBinding(HttpParameterDescriptor descriptor) :
base(descriptor) { }
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext context,
CancellationToken cancellationToken)
{
SetValue(context, new User() { // set properties here });
return Task.FromResult<object>(null);
}
}
Then, to use the binding, you will configure it, like this.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// snip
config.ParameterBindingRules.Insert(0, d =>
d.ParameterType == typeof(User) ? new UserParameterBinding(d) : null);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
With that, wherever you have User as action method parameter, it will automatically bind the instance you are creating inside UserParameterBinding to that parameter.
I'm working on an API where I'd like to be able to customize the response structure based on a parameter from the client. Response filters seem like a good place to do this in order to avoid doing so in each service or action. The problem is that while I have access to the response DTO returned by the action, and could change its properties, I can't find how or where to replace the object entirely.
Naively replacing the object in the response filter did not work, but this help illustrate what I'm trying to do:
public class ChangeResponseAttribute : ResponseFilterAttribute
{
public override void Execute(IHttpRequest req, IHttpResponse res, object responseDto)
{
var overrideText = req.QueryString["override"];
if (!String.IsNullOrEmpty(overrideText))
responseDto = new { Message = overrideText };
}
}
[ChangeResponse]
public class TodosService : Service
{
public object Get(Todos request)
{
return new object[0];
}
}
It looks like another option would be to write the custom response directly & end the request, but that would bypass any other processing left to do by ServiceStack.
Is there a better place to do this than a response filter? Or do I need to bite the bullet and return the optimal DTO in each action?
You can't change the Response DTO in a filter, but yes one option is to write the response in the filter itself (see this answer for an example of how to do this).
The other option is to use a ServiceRunner and override the OnAfterExecute() custom hook which does let you modify the response returned, e.g:
public class MyServiceRunner<T> : ServiceRunner<T>
{
public override object OnAfterExecute(
IRequestContext requestContext, object response)
{
// Called just after any Action is executed
var overrideText = req.Get<IHttpRequest>().QueryString["override"];
return !string.IsNullOrEmpty(overrideText)
? new { Message = overrideText } : null;
}
}
To get ServiceStack to use it you need to override the CreateServiceRunner method in your AppHost, e.g:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(
ActionContext actionContext)
{
return new MyServiceRunner<TRequest>(this, actionContext);
}