diff --git a/frappe/api.py b/frappe/api.py index 2ce490d68d..0bb33cfef6 100644 --- a/frappe/api.py +++ b/frappe/api.py @@ -164,23 +164,29 @@ def validate_auth_via_api_keys(): """ try: authorization_header = frappe.get_request_header("Authorization", None).split(" ") if frappe.get_request_header("Authorization") else None + authorization_source = frappe.get_request_header("Frappe-Authorization-Source", None) if authorization_header and authorization_header[0] == 'Basic': token = frappe.safe_decode(base64.b64decode(authorization_header[1])).split(":") - validate_api_key_secret(token[0], token[1]) elif authorization_header and authorization_header[0] == 'token': token = authorization_header[1].split(":") - validate_api_key_secret(token[0], token[1]) + + if authorization_source: + validate_api_key_secret(token[0], token[1], authorization_source) except Exception as e: raise e -def validate_api_key_secret(api_key, api_secret): - user = frappe.db.get_value( - doctype="User", +def validate_api_key_secret(api_key, api_secret, frappe_authorization_source=None): + """ frappe_authorization_source to provide api key and secret for a doctype apart from User """ + if not frappe_authorization_source: + frappe_authorization_source = 'User' + doc = frappe.db.get_value( + doctype=frappe_authorization_source, filters={"api_key": api_key}, - fieldname=['name'] + fieldname=["name"] ) form_dict = frappe.local.form_dict - user_secret = frappe.utils.password.get_decrypted_password ("User", user, fieldname='api_secret') - if api_secret == user_secret: + doc_secret = frappe.utils.password.get_decrypted_password(frappe_authorization_source, doc, fieldname="api_secret") + if api_secret == doc_secret: + user = frappe.db.get_value(frappe_authorization_source, doc, 'user') frappe.set_user(user) - frappe.local.form_dict = form_dict + frappe.local.form_dict = form_dict \ No newline at end of file diff --git a/frappe/frappeclient.py b/frappe/frappeclient.py index 1a79ca3618..e64ecc26f9 100644 --- a/frappe/frappeclient.py +++ b/frappe/frappeclient.py @@ -3,6 +3,7 @@ import requests import json import frappe from six import iteritems, string_types +from base64 import b64encode ''' FrappeClient is a library that helps you connect with other frappe systems @@ -18,7 +19,7 @@ class FrappeException(Exception): pass class FrappeClient(object): - def __init__(self, url, username=None, password=None, verify=True): + def __init__(self, url, username=None, password=None, verify=True, api_key=None, api_secret=None, frappe_authorization_source = None): self.headers = dict(Accept='application/json') self.verify = verify self.session = requests.session() @@ -28,6 +29,10 @@ class FrappeClient(object): if username and password: self._login(username, password) + # token based authentication if api_key and api_secret provided + elif api_key and api_secret: + self.authenticate(api_key, api_secret, frappe_authorization_source) + def __enter__(self): return self @@ -49,6 +54,14 @@ class FrappeClient(object): raise SiteExpiredError raise AuthError + def authenticate(self, api_key, api_secret, frappe_authorization_source=None): + token = b64encode('{}:{}'. format(api_key, api_secret)) + auth_header = {'Authorization': 'Basic {}'.format(token)} + self.session.headers.update(auth_header) + if frappe_authorization_source: + auth_source = {'Frappe-Authorization-Source': frappe_authorization_source} + self.session.headers.update(auth_source) + def logout(self): '''Logout session''' self.session.get(self.url, params={