TastyPie apply authentication only to PUT/POST - tastypie

How do apply authentication & authorization to PUT and POST methods in TastyPie? I know we can define authentication and authorization in Meta inner class on the resource but how do i let these two methods ONLY to pass thru authentication and authorization layer. I interested in SessionAuthentication and DjangoAuthrization btw.

You can create class based on DjangoAuthrization (add it to Meta then) and override two functions:
def read_list(self, object_list, bundle):
return object_list
def read_detail(self, object_list, bundle):
return True

Related

How do I load a "user" in a micronaut backend when JWT is provided

I have a Micronaut microservice that handles authentication via JsonWebTokens (JWT) from this guide.
Now I'd like to extend this code. The users in my app have some extra attributes such as email, adress, teamId etc. I have all users in the database.
How do I know in the backend controller method which user corresponds to the JWT that is sent by the client?
The guide contains this example code for the Micronaut REST controller:
#Secured(SecurityRule.IS_AUTHENTICATED)
#Controller
public class HomeController {
#Produces(MediaType.TEXT_PLAIN)
#Get
public String index(Principal principal) {
return principal.getName();
}
}
I know that I can get the name of the principal, ie. the username from the HttpRequest. But how do I get my additional attributes?
(Maybe I misunderstand JWT a bit???)
Are these JWT "claims" ?
Do I need to load the corresponding user by username from my DB table?
How can I verify that the sent username is actually valid?
edit Describing my usecase in more detail:
Security requirements of my use case
Do not expose valid information to the client
Validate everything the client (a mobile app) sends via REST
Authentication Flow
default oauth2 flow with JWTs:
Precondition: User is already registerd. Username, hash(password) and furhter attributes (email, adress, teamId, ..) are known on the backend.
Client POSTs username and password to /login endpoint
Client receives JWT in return, signed with server secret
On every future request the client sends this JWT as bearer in the Http header.
Backend validates JWT <==== this is what I want to know how to do this in Micronaut.
Questions
How to validate that the JWT is valid?
How to and where in which Java class should I fetch additional information for that user (the additional attributes). What ID should I use to fetch this information. The "sub" or "name" from the decoded JWT?
How do I load a “user” in a micronaut backend when JWT is provided?
I am reading this as you plan to load some kind of User object your database and access it in the controller.
If this is the case you need to hook into the place where Authentication instance is created to read the "sub" (username) of the token and then load it from the database.
How to extend authentication attributes with more details ?
By default for JWT authentication is created using JwtAuthenticationFactory and going more concrete default implementation is DefaultJwtAuthenticationFactory. If you plan to load more claims this could be done by replacing it and creating extended JWTClaimsSet or your own implementation of Authentication interface.
How do I access jwt claims ?
You need to check SecurityService -> getAuthentication() ->getAttributes(), it returns a map of security attributes which represent your token serialised as a map.
How to validate that the JWT is valid?
There is a basic validation rules checking the token is not expired and properly signed, all the rest validations especially for custom claims and validating agains a third parties sources have to be done on your own.
If you plan to validate your custom claims, I have already open source a project in this scope, please have a look.
https://github.com/traycho/micronaut-security-attributes
How to extend existing token with extra claims during its issuing ?
It is required to create your own claims generator extending JWTClaimsSetGenerator
#Singleton
#Replaces(JWTClaimsSetGenerator)
class CustomJWTClaimsSetGenerator extends JWTClaimsSetGenerator {
CustomJWTClaimsSetGenerator(TokenConfiguration tokenConfiguration, #Nullable JwtIdGenerator jwtIdGenerator, #Nullable ClaimsAudienceProvider claimsAudienceProvider) {
super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider)
}
protected void populateWithUserDetails(JWTClaimsSet.Builder builder, UserDetails userDetails) {
super.populateWithUserDetails(builder, userDetails)
// You your custom claims here
builder.claim('email', userDetails.getAttributes().get("email"));
}
}
How do I access jwt claims ?
If you want to access them from the rest handler just add io.micronaut.security.authentication.Authentication as an additional parameter in the handling method. Example
#Get("/{fooId}")
#Secured(SecurityRule.IS_AUTHENTICATED)
public HttpResponse<Foo> getFoo(long fooId, Authentication authentication) {
...
}
I found a solution. The UserDetails.attributes are serialized into the JWT. And they can easily be set in my CustomAuthenticationProviderclass:
#Singleton
#Slf4j
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Publisher<AuthenticationResponse> authenticate(
#Nullable HttpRequest<?> httpRequest,
AuthenticationRequest<?, ?> authenticationRequest)
{
// ... autenticate the request here ...
// eg. via BasicAuth or Oauth 2.0 OneTimeToken
// then if valid:
return Flowable.create(emitter -> {
UserDetails userDetails = new UserDetails("sherlock", Collections.emptyList(), "sherlock#micronaut.example");
// These attributes will be serialized as custom claims in the JWT
Map attrs = CollectionUtils.mapOf("email", email, "teamId", teamId)
userDetails.setAttributes(attrs);
emitter.onNext(userDetails);
emitter.onComplete();
}, BackpressureStrategy.ERROR);
}
}
And some more pitfalls when validating the JWT in the backend
A JWT in Micronaut MUST contain a "sub" claim. The JWT spec does not require this, but Micronaut does. The value of the "sub" claim will become the username of the created UserDetails object.
If you want to load addition attributes into these UserDetails when the JWT is validated in the backend, then you can do this by implementing a TokenValidator. But (another pitfal) then you must set its ORDER to a value larger than micronaut's JwtTokenValidator. Your order must be > 0 otherwise your TokenValidator will not be called at all.

Token-based Authentication with Cornice for Pyramid

I am using the Resources strategy of developing a RESTful API within a Pyramid App.
http://cornice.readthedocs.io/en/latest/resources.html. However I couldn't find an example of adding authentication for the API. Any guidance is greatly appreciated.
As Antoine Leclair pointed out, Cornice relies on Pyramid. You will have to enable an authorization and an authentication policies during your app initialization. For example (here using pyramid-jwt):
from pyramid.config import Configurator
from pyramid.authorization import ACLAuthorizationPolicy
def main():
config = Configurator()
# Pyramid requires an authorization policy to be active.
config.set_authorization_policy(ACLAuthorizationPolicy())
# Enable JWT authentication.
config.include('pyramid_jwt')
config.set_jwt_authentication_policy('secret')
You can also create your own policy, by inheriting from builtin Pyramid classes in pyramid.authentication:
from pyramid.authentication import CallbackAuthenticationPolicy
from pyramid.interfaces import IAuthenticationPolicy
from zope.interface import implementer
#implementer(IAuthenticationPolicy)
class MyAuthenticationPolicy(CallbackAuthenticationPolicy):
def __init__(self, realm='Realm'):
self.realm = realm
def unauthenticated_userid(self, request):
user_id = self._get_credentials(request)
return user_id
def forget(self, request):
return [('WWW-Authenticate', 'MyAuth realm="%s"' % self.realm)]
def _get_credentials(self, request):
authorization = request.headers.get('Authorization', '')
# your own strategy...
# if valid: return user_id
# else return None
Check out existing projects on awesome-pyramid to see if what you need is already there...

2 separate controllers for the same end point in html and json or a single one?

I have the end points "/customers" and "/api/v1/customers", in html and json respectively for a list of customers. Do I have to create 2 different controllers and thus actions for them? Or can I return html or json from a single controller and action depending a requested format: html or json? Note that for "/api/v1/customers" I need authentication via an Api Key.
You can have one controller and action for both endpoints, but I would advise against it.
You mentioned that those controllers need to do different stuff, so instead of adding stuff like "if json then check api key" make two separate controllers and extract common code of getting all the customers.
There is a great talk about untangling business logic from http interface: http://www.elixirconf.eu/elixirconf2016/lance-halvorsen Getting a list of customers might be out of your controllers, so at the end you will have two controllers like this:
defmodule MyApp.Api.CustomersController do
plug MaApp.ApiAuth #plug for checking api key
def index(conn, params) do
...
customers = ActualLogic.get_customers()
...
end
end
def MyApp.CustomersController do
plug MyApp.UserAuth #for example checks if user is logged in
def index(conn, params) do
...
customers = ActualLogic.get_customers()
...
end
end
At the end your controller does not perform any logic, it calls something else to do the job and action is responsible only for web stuff like parsing params, authentication via api key, session cookies and translating end result to json/html.

How to do Flask REST api key validation without decorators?

I'm new to Flask and I'm trying to accomplish the following:
For all of the subroutes of a particular route I want to extract a parameter and validate that an api key has been provided either by GET param or Header key.
The ideal would be if I could nest blueprints. Then I would do something like following:
Have a main blueprint to pull the parameter and validate the api key:
#secured_api.url_value_preprocessor
def pull_tenant(endpoint, values):
g.tenant_code = values.pop('tenant')
#secured_api.before_request
def validate_api_key():
api_key = request.headers.get('X-Api-Key')
...
if (api_key is None):
raise InvalidApiKey()
Then, having another blueprint with my resources (v1_bp) I could do:
secured_api.register_blueprint(v1_bp, url_prefix="/v1")
app.register_blueprint(secured_api, url_prefix='/secured/<tenant>')
So that all of v1_bp routes would be under /secured//v1
What would be the best way to achieve this?
Thanks in advance!

what API Gateway methods support Authorization?

When I create a resource/method in AWS API Gateway API I can create one of the following methods: DELETE, GET, HEAD, OPTIONS, PATCH or POST.
If I choose GET then API Gateway doesn't pass authentication details; but for POST it does.
For GET should I be adding the cognito credentials to the URL of my GET? or just never use GET and use POST for all authenticated calls?
My set-up in API Gateway/Lambda:
I created a Resource and two methods: GET and POST
Under Authorization Settings I set Authorization to AWS_AIM
For this example there is no Request Model
Under Method Execution I set Integration type to Lambda Function and I check Invoke with caller credentials (I also set Lambda Region and Lambda Function)
I leave Credentials cache unchecked.
For Body Mapping Templates, I set Content-Type to `application/json' and the Mapping Template to
{ "identity" : "$input.params('identity')"}
In my Python Lambda function:
def lambda_handler(event, context):
print context.identity
print context.identity.cognito_identity_id
return True
Running the Python function:
For the GET context.identity is None
For the POST context.identity has a value and context.identity.cognito_identity_id has the correct value.
As mentioned in comments: all HTTP methods support authentication. If the method is configured to require authentication, authentication results should be included in the context for you to access via mapping templates to pass down stream as contextual information.
If this is not working for you, please update your question to reflect:
How your API methods are configured.
What your mapping template is.
What results you see in testing.
UPDATE
The code in your lambda function is checking the context of the Lambda function, not the value from API Gateway. To access the value passed in from API Gateway, you would need to use event.identity not context.identity.
This would only half solve your problem as you are not using the correct value to access the identity in API gateway. That would be $context.identity.cognitoIdentityId (assuming you are using Amazon Cognito auth). Please see the mapping template reference for a full guide of supported variables.
Finally, you may want to consider using the template referenced in this question.