Pyramid security based on attribute of record - python

I have tables in DB with the same interface for view and edit them with Pyramid app. For example:
Example of route for view record of report table: /birdreport/report/871;
Example of route for edit record of report table: /birdreport/report/871/edit;
Each record of report table has field which contains user_id - this value is the same as returned by authenticated_userid function. It is clear for me how I can disable acces to edit by adding permission to view. But how I can enable access to edit view only for those users which userid presents in corresponding record?

You can use the Pyramid authorization policy by defining __acl__() inside your Report model. For example:
from sqlalchemy.orm import relationship, backref
from pyramid.security import Everyone, Allow
class Report(Base):
# ...
user_id = Column(Integer, ForeignKey('user.id'))
# ...
#property
def __acl__(self):
return [
(Allow, Everyone, 'view'),
(Allow, self.user_id, 'edit'),
]
# this also works:
#__acl__ = [
# (Allow, Everyone, 'view'),
# (Allow, self.user_id, 'edit'),
#]
class User(Base):
# ...
reports = relationship('Report', backref='user')
The __acl__() above will allow everyone to call your view view, but only the user related to Report to edit it.
It's likely that you haven't had authentication policy or authorization policy enabled, to quote the documentation:
Use the set_authorization_policy() method of the Configurator to enable an authorization policy.
You must also enable an authentication policy in order to enable the authorization policy. This is because authorization, in general, depends upon authentication. Use the set_authentication_policy() and method during application setup to specify the authentication policy.
from pyramid.config import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
authentication_policy = AuthTktAuthenticationPolicy('seekrit')
authorization_policy = ACLAuthorizationPolicy()
config = Configurator()
config.set_authentication_policy(authentication_policy)
config.set_authorization_policy(authorization_policy)
The above configuration enables a policy which compares the value of an “auth ticket” cookie passed in the request’s environment which contains a reference to a single principal against the principals present in any ACL found in the resource tree when attempting to call some view.
While it is possible to mix and match different authentication and authorization policies, it is an error to configure a Pyramid application with an authentication policy but without the authorization policy or vice versa. If you do this, you’ll receive an error at application startup time.

Related

How can I authenticate a user who belongs to another database through my other microservice in django rest framework?

I'm new to django and I am required to create two microservices with separate databases;
One to hold user information and the other to hold todo/tasks information. So far, I have created two separate projects with two separate databases,
To authenticate the user using simplejwt authentication. (todo_auth project with todo_auth database)
To show the todo/task information specific to that user. (todo project with todo database)
I need the todo project to verify the token by routing it back to the todo_auth project, and then I need the todo_auth project to send a response to the todo project. (By specifying the port)
How can I achieve this? Many thanks.
PS: I'm running the two django projects on the same server with different port numbers.
Simple JWT provides a verify route that you can pass a token to which will validate it was singed by the server and it is not expired.
From the documentation:
You can also include a route for Simple JWT’s TokenVerifyView if you wish to allow API users to verify HMAC-signed tokens without having access to your signing key:
from rest_framework_simplejwt.views import TokenVerifyView
urlpatterns = [
...
path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
...
]
If you want to do some other logic you should just write a normal view, use the JWT auth provided, and have the other one forward the token in the request
# todo-project
class ToDoView(APIView):
def get(self, request):
auth = request.headers["authorization"]
response = requests.get(
"http://todo-auth.sevice.com/api/do-thing/",
headers={
"Authorization": auth
}
)
if response.status_code = 200:
do_something(response.json())
# todo-auth-service
class DoThing(APIView):
authentication_classes = [JWTAuthentication]
def get(self, request):
...

Python Social Auth with Django - how to retrieve extra data?

For my first Django project I wanted to tie up Python Social Auth for the social authentication (namely Facebook).
Django==2.0
social-auth-app-django==2.1.0
social-auth-core==1.7.0
How can I retrieve extra data from the logged in user's profile? My goal is to filter the logged in users to custom groups based on FB groups they are members of. However at this point I can't even get the email, just the username.
In my settings.py:
SOCIAL_AUTH_FACEBOOK_KEY = 'xxxx'
SOCIAL_AUTH_FACEBOOK_SECRET = 'xxxx'
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email', 'groups_access_member_info']
SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {
'fields': 'id, name, email',
'edges': 'groups'
}
The pipeline is the basic pipline:
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
)
I'm not asking for complete code, any help would be much appreciated.
Thank you in advance!
python-social-auth will just store the basic user information it needs to fill the model fields, if anything extra is part of the auth payload from the provider, and it's configured in the EXTRA_DATA setting, it will also be stored in the social-related class as part of the extra_data attribute.
Still, python-social-auth won't call any other API in the provider to fetch additional data, for that to work, you need to enhance the PIPELINE with your methods that will call these additional endpoints on Facebook, once with the response, you can store it were it fits on your project.
To debug what's coming by default from the provider, add the debug pipeline between the steps (social_core.pipeline.debug.debug). If what you are looking for is already part of the payload, then take note of the key name and add it to the EXTRA_DATA setting. If it's not, then you need to add a method that will call Facebook API to retrieve the extra information.

Python Social Auth, Google, and refresh token

In a personal project, I am trying to use Django as my front end and then allow data entered by users in a particular form to be copied to google sheets.
Google's own docs recommend using https://github.com/google/oauth2client which is now deprecated, and the docs have not been updated. With this, I have started attempting to use Python Social Auth and Gspread. For Gspread to be able to function correctly, I need to be able to pass it not only an access token but also a refresh token. Python Social Auth however is not persisting the refresh token along with the rest of the "extra data". Looking at the data preserved and the URLs routed to, it seems to me more like somewhere it is routing through Google+.
I have the following configurations in my Django settings files:
AUTHENTICATION_BACKENDS = (
'social_core.backends.google.GoogleOAuth2',
'django.contrib.auth.backends.ModelBackend',
)
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
'social_core.pipeline.social_auth.associate_by_email',
)
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '...'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = '...'
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['https://www.googleapis.com/auth/spreadsheets']
Is there a better way to access a google sheet?
Am I correct that PSA or google is redirecting me into a Google+ auth flow instead of the Google Oauth2?
If not, what must change so that Python Social Auth keeps the refresh token?
It's true that python-social-auth will use some bits of the Google+ platform, at least the API to retrieve details about the user to fill in the account.
From your settings, I see you have associate_by_email at the bottom, at that point, at that point it has no use since the user is already be created, if you really plan to use it, it must be before the create_user one, you can check the DEFAULT_PIPELINE as a reference.
In order to get a refresh_token from google, you need to tell it that you want one, to do that you need to set the offline access type:
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {
'access_type': 'offline'
}
With that setting Google will give you a refresh_token and it will automatically stored in extra_data.
Just provide this in your settings.py:
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {
'access_type': 'offline',
'hd': 'xyzabc.com',
'approval_prompt':'force'
}
remeber there is {'approval_prompt' : 'force'} which will force the user to select the gmail account, this way you will get refresh token.
You can send extra parameters to the OAuth2 provider using the variable
SOCIAL_AUTH_<PROVIDER>_AUTH_EXTRA_ARGUMENTS
For Google, you can see the extra parameters they accept in their documentation (scroll down to "parameters"). The one we are looking for is access_type:
access_type: Indicates whether your application can refresh access tokens when the user is not present at the browser. Valid parameter values are online, which is the default value, and offline.
So we can add the following to settings.py, to indicate that we want to receive a refresh token:
SOCIAL_AUTH_GOOGLE_OAUTH2_EXTRA_ARGUMENTS = {"access_type: offline"}
The results from EXTRA_ARGUMENTS will be stored in extra_data, so the refresh token can be accessed like this:
refresh_token = user.social_auth.get(provider="google-oauth2").extra_data["refresh_token"]
One possible solution is to store the refresh token alongside the user in a UserProfile model, by adding a custom function to the social-auth pipeline:
Create the model
# models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
refresh_token = models.CharField(max_length=255, default="")
Add a function to access store the refresh token
# pipeline.py
from .models import UserProfile
def store_refresh_token(user=none, *args, **kwargs):
extra_data = user.social_auth.get(provider="google-oauth2").extra_data
UserProfile.objects.get_or_create(
user=user, defaults={"refresh_token": extra_data["refresh_token"]}
)
Add our new function to the social-auth pipeline.
# settings.py
...
SOCIAL_AUTH_PIPELINE = (
...
"my-app.pipeline.store_refresh_token"
)
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [
'https://www.googleapis.com/auth/spreadsheets'
# any other scopes you need
]
...
The token is now stored alongside the user and can be used to initialise the sheets client or whatever else you need.

How to get oauth2 client app from Django request

I'm using oauth2_provider + rest_framework. I have configured several client applications, and they successfully authenticate and receive access tokens.
I would like to have the client app in the request (Eg. request.client). Perhaps I should create some kind of middleware, which sets it, but I'm not sure what is the proper way to do it. Or maybe this functionality is already provided by the oauth2_provider/oauthlib, and I have overlooked it?
The client should be set when:
a valid access token is provided
valid app credentials are provided (like when requesting access token)
Python v3.5.3, Django v1.10.6
oauth2_provider AccessToken has a foreign key
to the application issued that token
You can get the application form the access token like this: application = request.auth.application
AbstractApplication class has foreign key to settings.AUTH_USER_MODEL https://github.com/evonove/django-oauth-toolkit/blob/0.12.0/oauth2_provider/models.py#L62
So if you are using default Application class you could get the clients by request.user.oauth2_providers_applications

Django Tastypie: How to Authenticate with API Key

I'm making an internal API with TastyPie. I have
from tastypie.authentication import ApiKeyAuthentication
class MyResource(ModelResource):
Meta:
authentication = ApiKeyAuthentication()
With Auth rules disabled, my API works great. With it on, I get a 401 (UNAUTHORIZED) response no matter what I try.
I'm sure this is one of those things that's really obvious once you've see it in action, but in the meantime, please advise how to to make the request (a GET).
Add the username and api_key parameters to your GET variables. Make sure that you have the
curl http://localhost:8000/api/v1/books/?username=issackelly\&api_key=123456789adfljafal
Make sure to follow the other instructions from teh docs when setting it up:
ApiKeyAuthentication
As an alternative to requiring sensitive data like a password, the ApiKeyAuthentication allows you to collect just username & a machine-generated api key. Tastypie ships with a special Model just for this purpose, so you'll need to ensure tastypie is in INSTALLED_APPS.
Tastypie includes a signal function you can use to auto-create ApiKey objects. Hooking it up looks like:
from django.contrib.auth.models import User
from django.db import models
from tastypie.models import create_api_key
models.signals.post_save.connect(create_api_key, sender=User)

Categories