tastypie: use PUT Partially Updating An Existing Resource succeed! why? - tastypie

when I use put,but not patch, successfully Partially updated my resource , I'm confused!
can put use as a patch?
according to django-tastypie readthedocs
A PUT request requires that the entire resource representation be enclosed. Missing fields may cause errors, or be filled in by default values.
my development env:
django 1.5
django-tastypie 0.9.15
a resource code looks like:
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'user'
excludes = ['email', 'password', 'is_active', 'is_staff', 'is_superuser']
#allowed_methods = ['get']
# no permissions checks are performed.
authentication = Authentication()
authorization = Authorization()

Related

How to implement dynamic creation of permission groups with different set of endpoints Django Rest Framework

In my project I have lot of endpoint views (APIViews, ViewSets). For all of them now I set permissions, some of them are default (e.g. AllowAny) and some are custom created:
permission_classes = (IsUserHaveSomePermission,)
Now I want to implement some flexible system, that will allow me to specify set of allowed endpoints for each user, for example:
On front-end I want to select some user and have a list of checkboxes that correspond to project's endpoints.
This is just an utopian solution, some details may be changed, but the main question is to how make something similar so that admins can basically dynamically change list of allowed endpoints/views for user?
thanks in advance
This solution can be implemented by storing if the user has permission to access the current request method and request path.
Create a new db model for storing the user, request method and request path. Lets say the name of the model is RequestPermission
Instead of the path you can store a constant representing the url so that you have the flexibility of editing the path later on. This constant can be the url name which is supported by django.
class RequestPermission(models.Model):
user = user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='request_permissions')
method = models.CharField(max_length=10)
path_name = models.CharField(max_length=200)
create a custom permission class:
class IsUserResuestAllowed(permissions.BasePermission):
def has_permission(self, request, view):
user = request.user
# you can choose how to get the path_name from the path
path_name = get_path_name(request.path)
return RequestPermission.objects.filter(user=user, method=request.method, path_name=path_name).exists()
Now you can use this class as the default permission class in rest framework settings or use it per view.

Correct configuration for REST endpoints in Shiro

My original post is here
I am trying to protect a set of REST endpoints with Shiro. My theory is that if I pass a JWT with my REST request, that I can use Shiro (via annotations) to secure my endpoints.
I've create my endpoints like this (for example):
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("status/{companyId}")
#RequiresAuthentication
#RequiresRoles("SomeRole")
public Response getStatus(#PathParam("companyId") int companyId){
... do stuff ...
}
I'm expecting that if I call the endpoint without authenticating, I will get a HTTP 401 error. However, the method is called successfully if the JWT is not supplied as it would be when there is no security on it at all.
I assume then that my Shiro config is incorrect. Since this is strictly a 'backend' application, I have no use for the Shiro/Stormpath configurations that apply to anything 'front-end' related (such as loginURLs, etc.)
Here is my shiro.ini :
[main]
#ERRORS IF UNCOMMENTED
#cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
#securityManager.cacheManager = $cacheManager
#stormpathClient.cacheManager = $cacheManager
# NOT NEEDED?
#sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
#securityManager.sessionManager = $sessionManager
#securityManager.sessionManager.sessionIdCookieEnabled = false
#securityManager.sessionManager.sessionIdUrlRewritingEnabled = false
[urls]
/** = rest
This configuration lets every request through (as described above).
If I uncomment the [main] section, I get IllegalArgumentException: Configuration error. Specified object [stormpathClient] with property [cacheManager] without first defining that object's class. Please first specify the class property first, e.g. myObject = fully_qualified_class_name and then define additional properties.
What I need to figure out is what is the correct minimum Shiro configuration for REST endpoints (and ONLY REST endpoints) so I can allow access with a JWT.
Thanks for any help.
I'm guessing the annotations are not being processed by anything at runtime. You will need to tell your JAX-RS app to process them.
I've done this with this lib in the past:
https://github.com/silb/shiro-jersey/
Specifically something like this:
https://github.com/silb/shiro-jersey/blob/master/src/main/java/org/secnod/shiro/jersey/AuthorizationFilterFeature.java
As for the second part of the problem, my only guess is Stormpath/Shiro environment is not setup correctly.
Did you put filter config in your web.xml or is all of the config loaded from the servlet fragment?

MembershipReboot with IdentityServer v3

I am having trouble extracting UserAccount properties from MembershipReboot in conjunction with Thinktecture IdentityServer. I have both up and running using the Sample repo here: https://github.com/identityserver/IdentityServer3.MembershipReboot
When I request the "openid profile" scope in an Implicit Grant Flow, I am missing a lot of the user account fields such as "given_name, middle_name", etc from the id_token and response from the userinfo endpoint. I understand this is because they need to be assigned in the GetClaimsFromAccount function.
I can see the requestedClaims come into the GetProfileDataAsync() function in the MembershipRebootUserService class and if I hover over the instance of TAccount in GetClaimsFromAccount I can see the Firstname, Lastname, etc properties appearing in the CustomUser dynamic proxy but I can't for the life of me work out how to access them and copy them into the claims collection?
More Info:
I suspect the issue is with this line:
claims.AddRange(userAccountService.MapClaims(account));
It looks like this should be converting the user account properties into claims but I dont get any back.
The way I understand it works is you add an option to your Scope object to return all of the claims for a user. IncludeAllClaimsForUser is the key property.
e.g.
new Scope
{
Enabled = true,
Name = "roles",
Type = ScopeType.Identity,
IncludeAllClaimsForUser = true,
Claims = new List<ScopeClaim>
{
new ScopeClaim("role")
}
}
My request includes the role property as well. This pulled back all the claims for the user from MR for me. My example is with Implicit flow btw.

Tastypie PUT : partial update on all objects in a resource

I have a django model like this, (BTW: I am using Tastypie)
class Watchlist(models.Model):
name = models.CharField(max_length=200)
created = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey('users.User')
stocks = models.ManyToManyField('Stock')
equityboss = models.BooleanField(blank=True, default=False)
last_watched = models.DateTimeField(auto_now_add=True)
order = models.IntegerField(max_length=10)
I want to update order field alone for all the objects in watchlist model.
I tried by sending PUT request, it erased all my records. I didn't get any error.
This is the data I send via PUT request
{'objects': [{'resource_uri': '/api/eboss/watchlist/2/', 'id': u'2', 'order': 0}, {'resource_uri': '/api/eboss/watchlist/1/', 'id': u'1', 'order': 1}]}
Please tell me what is the problem?
Look into HTTP PATCH (the RFC) - it's supported by Tastypie (their docs) and allows you to partially update a resource.
It looks like you will need to make individual PATCHes to each resource, however, as there is no mention of PATCHing a list in the same way you can PUT a new copy of the list.

PUT request in Tasty PIE to update using non PK value

I am using Tasty Pie to create Services. I have applied filtering so that it searches based on non PK value:
My api.py file :
class TestResource(ModelResource):
class Meta:
queryset = ack_dadc.objects.all()
authorization = Authorization()
filtering = {
'DistributorUID': ['exact'],
}
detail_allowed_methods = ['get', 'post', 'put', 'delete']
My question is:
Can I do a PUT request based on http:localhost/api/v1/test/?DistributorUID=steve
I have googled a lot but no luck I just want to update the database based on non PK value.
You'll find an example of how to achieve this using normal URL arguments in the Tastypie Cookbook: http://django-tastypie.readthedocs.org/en/latest/cookbook.html#using-non-pk-data-for-your-urls
I don't think you can do this : filters are only applied on GET requests.
So if you want to achieve this you have to do it with 2 requests :
GET http:localhost/api/v1/test/?DistributorUID=steve
to get the ID of the resource and then
PUT http:localhost/api/v1/test/ID
to update it based on its ID.