Token based authentication (#5752)
* token based authentication * authentication Basic base64encode(api_key:api_secret) token api_key:api_secret * test added Validation: only user with system manager can generate the keys * codacy issues fixed * token based authentication * authentication Basic base64encode(api_key:api_secret) token api_key:api_secret * test added Validation: only user with system manager can generate the keys * codacy issues fixed * use frappe.safe_encode * base64 encode use frappe.safe_encode * set frappe.local.form_dict after setting user * removed test * removed unused imports * test for python 3 * Update user.js * [user.py] throw correct error * Update user.py
This commit is contained in:
parent
b6ba3915a6
commit
abd3333bae
5 changed files with 2108 additions and 1849 deletions
|
|
@ -9,6 +9,8 @@ import frappe.client
|
|||
from frappe.utils.response import build_response
|
||||
from frappe import _
|
||||
from six.moves.urllib.parse import urlparse, urlencode
|
||||
import base64
|
||||
|
||||
|
||||
def handle():
|
||||
"""
|
||||
|
|
@ -35,6 +37,7 @@ def handle():
|
|||
"""
|
||||
|
||||
validate_oauth()
|
||||
validate_auth_via_api_keys()
|
||||
|
||||
parts = frappe.request.path[1:].split("/",3)
|
||||
call = doctype = name = None
|
||||
|
|
@ -149,3 +152,33 @@ def validate_oauth():
|
|||
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():
|
||||
"""
|
||||
authentication using api key and api secret
|
||||
|
||||
set user
|
||||
"""
|
||||
try:
|
||||
authorization_header = frappe.get_request_header("Authorization", None).split(" ") if frappe.get_request_header("Authorization") else 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])
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def validate_api_key_secret(api_key, api_secret):
|
||||
user = frappe.db.get_value(
|
||||
doctype="User",
|
||||
filters={"api_key": api_key},
|
||||
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:
|
||||
frappe.set_user(user)
|
||||
frappe.local.form_dict = form_dict
|
||||
|
|
@ -189,6 +189,19 @@ frappe.ui.form.on('User', {
|
|||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
generate_keys: function(frm){
|
||||
frappe.call({
|
||||
method: 'frappe.core.doctype.user.user.generate_keys',
|
||||
args: {
|
||||
user: frm.doc.name
|
||||
},
|
||||
callback: function(r){
|
||||
if(r.message){
|
||||
frappe.msgprint(__("Save API Secret: ") + r.message.api_secret);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -236,4 +249,4 @@ frappe.ModuleEditor = Class.extend({
|
|||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1058,3 +1058,24 @@ def create_contact(user):
|
|||
"phone": user.phone,
|
||||
"mobile_no": user.mobile_no
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def generate_keys(user):
|
||||
"""
|
||||
generate api key and api secret
|
||||
|
||||
:param user: str
|
||||
"""
|
||||
if "System Manager" in frappe.get_roles():
|
||||
user_details = frappe.get_doc("User", user)
|
||||
api_secret = frappe.generate_hash(length=15)
|
||||
# if api key is not set generate api key
|
||||
if not user_details.api_key:
|
||||
api_key = frappe.generate_hash(length=15)
|
||||
user_details.api_key = api_key
|
||||
user_details.api_secret = api_secret
|
||||
user_details.save()
|
||||
|
||||
return {"api_secret": api_secret}
|
||||
frappe.throw(frappe._("Not Permitted"), frappe.PermissionError)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ from __future__ import unicode_literals
|
|||
|
||||
import unittest, frappe, os
|
||||
from frappe.utils import get_url
|
||||
from frappe.core.doctype.user.user import generate_keys
|
||||
|
||||
import requests
|
||||
import base64
|
||||
|
||||
|
||||
class TestAPI(unittest.TestCase):
|
||||
def test_insert_many(self):
|
||||
|
|
@ -25,3 +30,39 @@ class TestAPI(unittest.TestCase):
|
|||
self.assertTrue(frappe.db.get_value('ToDo', {'description': 'Test API 1'}))
|
||||
self.assertTrue(frappe.db.get_value('ToDo', {'description': 'Test API 2'}))
|
||||
self.assertTrue(frappe.db.get_value('ToDo', {'description': 'Test API 3'}))
|
||||
|
||||
def test_auth_via_api_key_secret(self):
|
||||
|
||||
# generate api ke and api secret for administrator
|
||||
keys = generate_keys("Administrator")
|
||||
frappe.db.commit()
|
||||
generated_secret = frappe.utils.password.get_decrypted_password(
|
||||
"User", "Administrator", fieldname='api_secret'
|
||||
)
|
||||
|
||||
api_key = frappe.db.get_value("User", "Administrator", "api_key")
|
||||
header = {"Authorization": "token {}:{}".format(api_key, generated_secret)}
|
||||
res = requests.post(frappe.get_site_config().host_name + "/api/method/frappe.auth.get_logged_user", headers=header)
|
||||
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual("Administrator", res.json()["message"])
|
||||
self.assertEqual(keys['api_secret'], generated_secret)
|
||||
|
||||
header = {"Authorization": "Basic {}".format(base64.b64encode(frappe.safe_encode("{}:{}".format(api_key, generated_secret))).decode())}
|
||||
res = requests.post(frappe.get_site_config().host_name + "/api/method/frappe.auth.get_logged_user", headers=header)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual("Administrator", res.json()["message"])
|
||||
|
||||
# Valid api key, invalid api secret
|
||||
api_secret = "ksk&93nxoe3os"
|
||||
header = {"Authorization": "token {}:{}".format(api_key, api_secret)}
|
||||
res = requests.post(frappe.get_site_config().host_name + "/api/method/frappe.auth.get_logged_user", headers=header)
|
||||
self.assertEqual(res.status_code, 403)
|
||||
|
||||
|
||||
# random api key and api secret
|
||||
api_key = "@3djdk3kld"
|
||||
api_secret = "ksk&93nxoe3os"
|
||||
header = {"Authorization": "token {}:{}".format(api_key, api_secret)}
|
||||
res = requests.post(frappe.get_site_config().host_name + "/api/method/frappe.auth.get_logged_user", headers=header)
|
||||
self.assertEqual(res.status_code, 401)
|
||||
Loading…
Add table
Reference in a new issue