Access Control in Pylons App? - pylons

Do you know of any class for Pylons to control access for each view?
Thanks for the info! :)

You can use Authkit ( http://authkit.org ) and "authorize" decorator:
from authkit.authorize.pylons_adaptors import authorize
from authkit.permissions import RemoteUser
class MainController(BaseController):
#authorize(RemoteUser())
def index(self):
pass
You can write your own permission class, ex. (this is part of some old project, check it if you want use it):
class HasPerm(RequestPermission):
def __init__(self, perms, all=False, error=None):
if isinstance(perms, str):
perms = [perms]
self.all = all
self.perms = perms
self.error = error
self.full_access = "ADMIN"
def check(self, app, environ, start_response):
if not environ.has_key('REMOTE_USER'):
if self.error:
raise self.error
raise NotAuthenticatedError('Not authenticated')
user = Session.query(User)
user = user.filter_by(name=environ['REMOTE_USER']).first()
if not user:
raise NotAuthorizedError('No such user')
if user.blocked:
raise NotAuthorizedError('User blocked')
user_perms = [x.name for x in user.permissions]
if self.full_access in user_perms:
return app(environ, start_response)
for p in self.perms:
checked_perm = model.Permission.get_by(name=p)
if not checked_perm:
raise NotAuthorizedError("There is no permission")
if checked_perm.name in user_perms and not self.all:
return app(environ, start_response)
if checked_perm.name not in user_perms and self.all:
raise NotAuthorizedError("User has no permission")
raise NotAuthorizedError("User has no permission")

Related

Token is not recognized after authentication in FastAPI

I am creating a basic Authentication register/login with FastAPI. However, after the user has succesfully registered and logged in, the token does not get recognized. It works fine using the "/docs" through Swagger UI, but not from the main app.
Here is my code: main.py
import uvicorn
from fastapi import Depends, HTTPException
from auth import AuthHandler
from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
auth_handler = AuthHandler()
users = []
#app.get('/', response_class=HTMLResponse)
def get_register_form(request: Request):
return templates.TemplateResponse("register.html", {"request": request})
#app.post('/', response_class=HTMLResponse)
def register(request: Request, username: str = Form(...), password: str = Form(...)):
if len(users) != 0:
for x in users:
if x['username'] == username:
print('Username is taken!')
raise HTTPException(status_code=400, detail='Username is taken!')
hashed_password = auth_handler.get_password_hash(password)
users.append({
'username': username,
'password': hashed_password
})
print('User:', username, 'registered!')
return templates.TemplateResponse("success.html", {"request": request})
#app.get('/login', response_class=HTMLResponse)
def get_login_form(request: Request):
return templates.TemplateResponse("login.html", {"request": request})
#app.post('/login')
def login(request: Request, username: str = Form(...), password: str = Form(...)):
user = None
for x in users:
if x['username'] == username:
user = x
break
if (user is None) or (not auth_handler.verify_password(password, user['password'])):
print('Invalid username and/or password!')
raise HTTPException(status_code=401, detail='Invalid username and/or password!')
token = auth_handler.encode_token(user['username'])
return {'token': token}
#app.get('/protected')
def protected(username=Depends(auth_handler.auth_wrapper)):
return {'name': username}
if __name__ == '__main__':
uvicorn.run(app)
Here is my code: auth.py
import jwt
from fastapi import HTTPException, Security
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from passlib.context import CryptContext
from datetime import datetime, timedelta
class AuthHandler():
security = HTTPBearer()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
secret = 'SECRET'
def get_password_hash(self, password):
return self.pwd_context.hash(password)
def verify_password(self, plain_password, hashed_password):
return self.pwd_context.verify(plain_password, hashed_password)
def encode_token(self, user_id):
payload = {
'exp': datetime.utcnow() + timedelta(days=0, minutes=5),
'iat': datetime.utcnow(),
'sub': user_id
}
return jwt.encode(
payload,
self.secret,
algorithm='HS256'
)
def decode_token(self, token):
try:
payload = jwt.decode(token, self.secret, algorithms=['HS256'])
return payload['sub']
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail='Signature has expired')
except jwt.InvalidTokenError as e:
raise HTTPException(status_code=401, detail='Invalid token')
def auth_wrapper(self, auth: HTTPAuthorizationCredentials = Security(security)):
return self.decode_token(auth.credentials)
Here is my forms.html: register.html and login.html are the same.
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="../static/styles.css">
<title>Document</title>
</head>
<body>
<div id="form">
<form method="post">
<h3>Login</h3>
<label for="username">Username:</label><br>
<input type="text" name="username" id="username"><br>
<label for="password">Password:</label><br>
<input type="text" name="password" id="password"><br><br>
<input type="submit" value="Submit" id="sub">
</form>
</div>
</body>
</html>
The error I get when going to 127.0.0.1/protected is:
{"detail":"Not authenticated"}
How can I fix this, so that it recognizes the token from the user just like in docs?
I found a solution on the advise from #MatsLindh, I also simplified a lot the code, imports, etc.
You will obviously still need to have the html files with the form and fields you'll need. In my case I just added the email and password.
Please note that this is not "best practice" especially inserting in the same table of the database the password even it it's hashed.
import uvicorn
import sqlite3
import jwt
from datetime import datetime, timedelta
from fastapi import FastAPI, Form, HTTPException, Cookie, Request, Depends
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from passlib.context import CryptContext
connection = sqlite3.connect("users.db", check_same_thread=False)
cursor = connection.cursor()
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
class AuthHandler:
secret = 'SECRET' # I can put any key I want, it's going to be used to encrypt and decrypt
bcrypt_obj = CryptContext(schemes=["bcrypt"], deprecated="auto")
def get_password_hash(self, password):
return self.bcrypt_obj.hash(password)
def verify_password(self, plain_password, hashed_password):
return self.bcrypt_obj.verify(plain_password, hashed_password)
def encode_token(self, user_id):
payload = {
'exp': datetime.utcnow() + timedelta(days=0, minutes=5),
'iat': datetime.utcnow(),
'sub': user_id
}
return jwt.encode(
payload,
self.secret,
algorithm='HS256'
)
def decode_token(self, token):
try:
payload = jwt.decode(token, self.secret, algorithms=['HS256'])
return payload['sub']
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail='Signature has expired')
except jwt.InvalidTokenError as e:
raise HTTPException(status_code=401, detail='Invalid token')
def grant_access(self, token, users_db):
for x in users_db:
if x['email'] == self.decode_token(token):
return True
else:
return False
auth_handler = AuthHandler()
#app.get('/', response_class=HTMLResponse)
def get_register_form(request: Request):
return templates.TemplateResponse("register.html", {"request": request})
#app.post('/', response_class=HTMLResponse)
def register(email: str = Form(...), password: str = Form(...)):
cursor.execute("select * from users where email=:e", {"e": email})
user_with_same_email_list = cursor.fetchall()
if len(user_with_same_email_list) != 0:
print(user_with_same_email_list)
print('Email already registered!')
raise HTTPException(status_code=400, detail='Email already registered!')
else:
hashed_password = auth_handler.get_password_hash(password)
sqlite_insert_query = "INSERT INTO users (user_id, username, email, hashed_password, eoa) VALUES " \
"(1,'','" + email + "','" + hashed_password + "','')"
cursor.execute(sqlite_insert_query)
connection.commit()
print('User with email:', email, 'registered!')
response = RedirectResponse(url="/login")
response.status_code = 302
return response
#app.get('/login', response_class=HTMLResponse)
def get_login_form(request: Request):
return templates.TemplateResponse("login.html", {"request": request})
#app.post('/login')
def login(email: str = Form(...), password: str = Form(...)):
cursor.execute("select * from users where email=:e", {"e": email})
user_with_same_email_list = cursor.fetchall()
if len(user_with_same_email_list) == 0:
print('No user with this email!')
raise HTTPException(status_code=400, detail='No user with this email!')
elif (email is None) or (not auth_handler.verify_password(password, user_with_same_email_list[0][3])):
print('Invalid email and/or password!')
raise HTTPException(status_code=401, detail='Invalid email and/or password!')
else:
token = auth_handler.encode_token(email)
response = RedirectResponse(url="/check_cookie")
response.status_code = 302
response.set_cookie(key="Authorization", value=token, secure=True, httponly=True)
return response
#app.get("/check_cookie")
async def check_cookie(Authorization: str | None = Cookie(None)):
if Authorization:
email = auth_handler.decode_token(Authorization)
cursor.execute("select * from users where email=:e", {"e": email})
user_with_same_email_list = cursor.fetchall()
if len(user_with_same_email_list) == 0:
print('Invalid token')
raise HTTPException(status_code=401, detail='Invalid token')
else:
print('Access granted!')
response = RedirectResponse(url="/protected")
response.status_code = 302
return response
else:
print("No token found")
raise HTTPException(status_code=401, detail='No token found')
#app.get('/protected', response_class=HTMLResponse)
async def protected(request: Request, email=Depends(check_cookie)):
return templates.TemplateResponse("logged_in.html", {"request": request})
#app.get("/logged_out", response_class=HTMLResponse)
async def logged_out(request: Request):
return templates.TemplateResponse("logged_out.html", {"request": request})
#app.get("/logout")
async def route_logout_and_remove_cookie():
response = RedirectResponse(url="/logged_out")
response.delete_cookie("Authorization", domain="127.0.0.1")
return response
if __name__ == '__main__':
uvicorn.run(app)

How to take file input graphene-django

Graphql Mutations
class UploadMutation(graphene.Mutation):
class Arguments:
file = Upload(required=True)
success = graphene.Boolean()
def mutate(self, info, file, **kwargs):
if not file.endswith('.xlsx'):
raise GraphQLError('File formate should be "xlsx".')
return UploadMutation(success=True)
class FileUploadMutation(graphene.ObjectType):
upload_file = UploadMutation.Field()
I'm using below GraphQL mutations But I'm unable to upload files
mutation UploadFile{
uploadFile(file: xxx){
success
}
}
Please Help me out

Messages in Django Channels sent outside consumer not being received

I'm trying to send messages to specific websocket instances, but neither channel_layer.send, nor using channel_layer.group_send with unique groups for each instance seems to be working, no errors are being raised, they just aren't received by the instances.
The function that sends the message is:
def listRequest(auth_user, city):
request_country = city["country_name"]
request_city = city["city"]
request_location = request_city +", "+request_country
concatenate_service_email = auth_user.service + "-" + auth_user.email
this_request = LoginRequest(service_and_email=concatenate_service_email, location=request_location)
this_request.generate_challenge()
this_request.set_expiry(timezone.now() + timezone.timedelta(minutes=5))
this_request.save()
channel_layer = get_channel_layer()
print(auth_user.current_socket)
async_to_sync(channel_layer.group_send)(
auth_user.current_socket,{
"type": "new.request",
"service_and_email" : concatenate_service_email
},
)
My current working consumers.py (receive and scanrequest don't have anything that's likely to be relevant to the issue):
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
from .helpers import verify_identity, unique_group
from django.utils import timezone
from .models import Authentication, LoginRequest
import json
import time
class AuthConsumer(WebsocketConsumer):
account_list =[]
def connect(self):
print("Connect attempted")
print(self.channel_name)
print(unique_group(self.channel_name))
async_to_sync(self.channel_layer.group_add)(unique_group(self.channel_name), self.channel_name)
self.accept()
def disconnect(self, close_code):
print("Disconnect attempted")
async_to_sync(self.channel_layer.group_discard)(unique_group(self.channel_name), self.channel_name)
for i in self.account_list:
serviceEmailSplit = i.split("-")
try:
auth_user = Authentication.objects.get(service=serviceEmailSplit[0],email=serviceEmailSplit[1])
auth_user.set_socket("NONE")
auth_user.save()
except:
print("Error user %s does not exist" %i)
pass
def receive(self, text_data):
print("Receiving data")
if text_data[0:7] == "APPROVE":
data_as_list = text_data.split(",")
serviceEmailSplit = data_as_list[1].split("-")
auth_user = Authentication.objects.get(service=serviceEmailSplit[0],email=serviceEmailSplit[1])
this_request = LoginRequest.objects.get(service_and_email=data_as_list[1],approved=False, expiry__gt=timezone.now())
if verify_identity(auth_user.public_key, data_as_list[2], this_request.challenge):
this_request.set_approved()
self.send("Request Approved!")
else:
self.send("ERROR: User verification failed")
else:
self.account_list = text_data.split(",")
self.account_list.pop(-1)
print(self.account_list)
for i in self.account_list:
serviceEmailSplit = i.split("-")
try:
auth_user = Authentication.objects.get(service=serviceEmailSplit[0],email=serviceEmailSplit[1])
auth_user.set_socket(unique_group(self.channel_name))
auth_user.save()
except:
self.send("Error user %s does not exist" %i)
self.scanRequest()
def scanRequest(self):
requestSet = LoginRequest.objects.filter(service_and_email__in = self.account_list, approved = False, request_expiry__gt = timezone.now())
if requestSet.count() > 0:
for request in requestSet:
self.send(request.service_and_email+","+request.location+","+str(request.challenge))
else:
self.send("NOREQUESTS")
def new_request(self,event):
print("NEW REQUEST!")
this_request = LoginRequest.objects.filter(service_and_email = event["service_and_email"]).latest('request_expiry')
self.send(this_request.service_and_email+","+this_request.location+","+str(this_request.challenge))
And my routing.py:
from django.urls import re_path
from . import consumers
from django.conf.urls import url
websocket_urlpatterns = [
url(r"^ws/$", consumers.AuthConsumer.as_asgi()),
]
"NEW REQUEST!" is never printed, having tried to call it both by sending a message directly, and neither does using groups like I have written above.
My redis server appears to be working from testing like the documentation for the channels tutorial suggests:
https://channels.readthedocs.io/en/stable/tutorial/part_2.html
I'm pretty stumped after attempts to fix it, and I've looked at the other posts on stackoverflow with the same/similar issues and I'm already following whatever solutions they have in my code.

Dynamically add path in Groovy

I'm trying to add new functionality, without refactoring the entire code. Actually the code calls msg.typeOfMsg('msg'), but I want to add validation when it comes true change all the typeOfMsg to a specific type:
def specialRule = true
def message = changeMessageType()
def changeMessageType() {
def toInform = { def message -> thePath.toChange.inform(message) }
return specialRule ? [reprove : toInform, approve : toInform] : thePath.toChange
}
println(message.reprove('Standart reprove message')
This is the solution I found, it works, but I have a lot of message types and custom message types, there's some way to dynamically change all calls to inform, without refactoring all the code to calls something like message(typeOfMessage, message)?
-----Edit-----
Here's a runnable version
def message = changeMessageType()
def changeMessageType() {
def originalPath = new OriginalPath()
def specialRule = true
def toInform = { def message -> originalPath.inform(message) }
return specialRule ? [reprove : toInform, approve : toInform] : originalPath
}
message.approve('Standart reprove message')
class OriginalPath {
def reprove(message) {
println "Reprove: ${message}"
}
def approve(message) {
println "Approve: ${message}"
}
def inform(message) {
println "Inform: ${message}"
}
}

How to return access token on login with oauth2 in drf?

I want to return the user access token for oauth2 as soon as the user logs in with a login api.
Till now I have created a login and register api and I am able to genereate access token via /o/token but I want it as a return value.
Here is my views.py :-
"""
POST auth/login/
"""
# This permission class will overide the global permission
# class setting
permission_classes = (AllowAny,)
serializer_class = UserSerializer
queryset = User.objects.all()
def post(self, request, *args, **kwargs):
username = request.data.get("username", "")
password = request.data.get("password", "")
user = authenticate(request, username=username, password=password)
if user is not None:
# login saves the user’s ID in the session,
# using Django’s session framework.
login(request, user)
return redirect('list-user')
return Response(status=status.HTTP_401_UNAUTHORIZED)
class RegisterUserView(generics.CreateAPIView):
"""
POST auth/register/
"""
permission_classes = (AllowAny,)
serializer_class = UserRegistrationSerializer
def post(self, request, *args, **kwargs):
username = request.data.get("username", "")
password = request.data.get("password", "")
email = request.data.get("email", "")
if not username and not password and not email:
return Response(
data={
"message": "username, password and email is required to register a user"
},
status=status.HTTP_400_BAD_REQUEST
)
new_user = User.objects.create_user(
username=username, password=password, email=email
)
return Response(status=status.HTTP_201_CREATED)
and here is my serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'password']
class UserRegistrationSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email','password']
urls.py
path('admin/', admin.site.urls),
path('', include('users.urls')),
path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
]
urls.py for users app
urlpatterns = [
path('users/', views.UserListView.as_view(), name='list-user'),
path('auth/login/', views.LoginView.as_view(), name="auth-login"),
path('auth/register/', views.RegisterUserView.as_view(), name="auth-register")
]
How can I implement it?
You will have to get token from oauth2_provider view for auth token generation and and then you can modify response according to your requirement.
from oauth2_provider.views.base import TokenView
class CustomAuthView(generics.CreateAPIView):
permission_classes = (AllowAny,)
serializer_class = UserSerializer
def post(self, request, *args, **kwargs):
oauth_response = TokenView.as_view(request, *args, **kwargs)
if oauth_response.status == 200:
data = oauth_response.data
# update data according to your requirement
return response.Response(data)
else:
return oauth_response
If you want to just change url for auth view, then you can do it by adding a new url which will point towards TokenView like this
from oauth2_provider.views.base import TokenView
path('auth/login/', TokenView.as_view(), name="auth-login"),
You will have to provide following parameters to your api
grant_type
username
password
client_id
client_secret

Resources