Merge pull request #12895 from revant/feat-auth-hooks

feat(hooks): auth hooks
This commit is contained in:
mergify[bot] 2021-04-23 06:33:27 +00:00 committed by GitHub
commit 85cbdf7683
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 32 additions and 32 deletions

View file

@ -1,12 +1,10 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import base64
import binascii
import json
from six.moves.urllib.parse import urlencode, urlparse
from urllib.parse import urlencode, urlparse
import frappe
import frappe.client
@ -14,6 +12,7 @@ import frappe.handler
from frappe import _
from frappe.utils.response import build_response
def handle():
"""
Handler for `/api` methods
@ -38,9 +37,6 @@ def handle():
`/api/resource/{doctype}/{name}?run_method={method}` will run a whitelisted controller method
"""
validate_auth()
parts = frappe.request.path[1:].split("/",3)
call = doctype = name = None
@ -116,7 +112,7 @@ def handle():
frappe.local.form_dict['fields'] = json.loads(frappe.local.form_dict['fields'])
frappe.local.form_dict.setdefault('limit_page_length', 20)
frappe.local.response.update({
"data": frappe.call(
"data": frappe.call(
frappe.client.get_list,
doctype,
**frappe.local.form_dict
@ -140,6 +136,7 @@ def handle():
return build_response("json")
def get_request_form_data():
if frappe.local.form_dict.data is None:
data = frappe.safe_decode(frappe.local.request.get_data())
@ -148,25 +145,18 @@ def get_request_form_data():
return frappe.parse_json(data)
def validate_auth():
if frappe.get_request_header("Authorization") is None:
return
VALID_AUTH_PREFIX_TYPES = ['basic', 'bearer', 'token']
VALID_AUTH_PREFIX_STRING = ", ".join(VALID_AUTH_PREFIX_TYPES).title()
"""
Authenticate and sets user for the request.
"""
authorization_header = frappe.get_request_header("Authorization", str()).split(" ")
authorization_type = authorization_header[0].lower()
if len(authorization_header) == 1:
frappe.throw(_('Invalid Authorization headers, add a token with a prefix from one of the following: {0}.').format(VALID_AUTH_PREFIX_STRING), frappe.InvalidAuthorizationHeader)
if authorization_type == "bearer":
if len(authorization_header) == 2:
validate_oauth(authorization_header)
elif authorization_type in VALID_AUTH_PREFIX_TYPES:
validate_auth_via_api_keys(authorization_header)
else:
frappe.throw(_('Invalid Authorization Type {0}, must be one of {1}.').format(authorization_type, VALID_AUTH_PREFIX_STRING), frappe.InvalidAuthorizationPrefix)
validate_auth_via_hooks()
def validate_oauth(authorization_header):
@ -177,8 +167,8 @@ def validate_oauth(authorization_header):
authorization_header (list of str): The 'Authorization' header containing the prefix and token
"""
from frappe.oauth import get_url_delimiter
from frappe.integrations.oauth2 import get_oauth_server
from frappe.oauth import get_url_delimiter
form_dict = frappe.local.form_dict
token = authorization_header[1]
@ -192,14 +182,13 @@ def validate_oauth(authorization_header):
try:
required_scopes = frappe.db.get_value("OAuth Bearer Token", token, "scopes").split(get_url_delimiter())
valid, oauthlib_request = get_oauth_server().verify_request(uri, http_method, body, headers, required_scopes)
if valid:
frappe.set_user(frappe.db.get_value("OAuth Bearer Token", token, "user"))
frappe.local.form_dict = form_dict
except AttributeError:
frappe.throw(_("Invalid Bearer token, please provide a valid access token with prefix 'Bearer'."), frappe.InvalidAuthorizationToken)
pass
valid, oauthlib_request = get_oauth_server().verify_request(uri, http_method, body, headers, required_scopes)
if valid:
frappe.set_user(frappe.db.get_value("OAuth Bearer Token", token, "user"))
frappe.local.form_dict = form_dict
def validate_auth_via_api_keys(authorization_header):
@ -222,8 +211,7 @@ def validate_auth_via_api_keys(authorization_header):
except binascii.Error:
frappe.throw(_("Failed to decode token, please provide a valid base64-encoded token."), frappe.InvalidAuthorizationToken)
except (AttributeError, TypeError, ValueError):
frappe.throw(_("Invalid token, please provide a valid token with prefix 'Basic' or 'Token'."), frappe.InvalidAuthorizationToken)
pass
def validate_api_key_secret(api_key, api_secret, frappe_authorization_source=None):
@ -248,3 +236,8 @@ def validate_api_key_secret(api_key, api_secret, frappe_authorization_source=Non
if frappe.local.login_manager.user in ('', 'Guest'):
frappe.set_user(user)
frappe.local.form_dict = form_dict
def validate_auth_via_hooks():
for auth_hook in frappe.get_hooks('auth_hooks', []):
frappe.get_attr(auth_hook)()

View file

@ -56,6 +56,7 @@ def application(request):
frappe.recorder.record()
frappe.monitor.start()
frappe.rate_limiter.apply()
frappe.api.validate_auth()
if request.method == "OPTIONS":
response = Response()

View file

@ -9,7 +9,6 @@ import frappe
import frappe.utils
import frappe.sessions
from frappe.utils import cint
from frappe.api import validate_auth
from frappe import _, is_whitelisted
from frappe.utils.response import build_response
from frappe.utils.csvutils import build_csv_response
@ -24,7 +23,7 @@ ALLOWED_MIMETYPES = ('image/png', 'image/jpeg', 'application/pdf', 'application/
def handle():
"""handle request"""
validate_auth()
cmd = frappe.local.form_dict.cmd
data = None

View file

@ -312,6 +312,13 @@ user_data_fields = [
}}
]
# Authentication and authorization
# --------------------------------
# auth_hooks = [
# "{app_name}.auth.validate"
# ]
"""
desktop_template = """# -*- coding: utf-8 -*-