Social login refactor (#4519)

* Added DocType Social Login Key

WIP for https://github.com/frappe/frappe/issues/4496
added basic fields
after_insert add provider_username and provider_userid fields on User dt
on_trash deletes added fields on User dt

* Added field to store fontawesome icon for provider

* [Patch] Social Login Keys to Social Login Key

* [Patch] Social Login Keys to Social Login Key

* Social Login Key generates boilerplate

* patch fixed for social_login_refactor

* removed patch-not working

* use social login keys to initiate flow

* Login page shows Social Login Key

* show login via if base_url present

* removed boilerplate generator

* Multiple Changes

fix zxcvbn import in password_strength.py
use of child table instead of additional fields on user dt to store username and userid

* Fetched Template on Client JS

* Frappe social login template working

* Added Social Login Key Templates

* Codacy fixes and validate social login key urls

* [Patch] Social Login Keys (untested)

* [Fix] Patch refactor social login keys

* [Fix] Patch refactor_social_login_keys manually tested

* Refactor OAuth 2.0 related changes for Social Login Key

* [Fix] Patch refactor social login keys

* Test - Adding Frappe Social Login Key

* Social Login Key Tests

check added child table entry on user for provider frappe
it also checks if userid is created

* [WIP] Office 365 Social Login Key Template

* [Fix] Social Login - Redirect URL

* [Test] Single sign-on icons for added provider

* [Fix] Codacy Errors

* [Fix] Social Login Key Form JS

* Docs Added for Social Login Key

* [Fix] Patch Refactor Social Login Keys

* Handle different icon types

Handle different icon types (image, icon, emoji) with just icon field

* Move the login methods to a new py file

frappe.integrations.oauth2_logins added
copied whitelisted guest oauth2 redirect endpoints from login.py
removing the functions from login.py will break backward compatibility

* Social Login Key Form Changes

Moved Enable field to top
Fields which are not editable are collapsed

* [Fix] Codacy Errors

* Corrected Docs, sync.py

* [Docs] Adding a social login provider

* [Fix] set frappe userid from User Social Login

* [Fix] frappe userid in oauth.py

* removed icon_type

* Use frappe.utils.is_image
This commit is contained in:
Revant Nandgaonkar 2018-01-03 14:57:16 +05:30 committed by Faris Ansari
parent ba9f53bcce
commit 02aa7b6f41
35 changed files with 1555 additions and 776 deletions

View file

@ -44,7 +44,7 @@ def get_data():
"items": [
{
"type": "doctype",
"name": "Social Login Keys",
"name": "Social Login Key",
"description": _("Enter keys to enable login via Facebook, Google, GitHub."),
},
{

View file

@ -26,6 +26,10 @@ class TestUser(unittest.TestCase):
first_name='Tester')).insert()
self.assertEquals(new_user.user_type, 'Website User')
# social login userid for frappe
self.assertTrue(new_user.social_logins[0].userid)
self.assertEquals(new_user.social_logins[0].provider, "frappe")
# role with desk access
new_user.add_roles('_Test Role 2')
new_user.save()

View file

@ -1851,95 +1851,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "fb_username",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Facebook Username",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "fb_userid",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Facebook User ID",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "google_userid",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Google User ID",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_49",
"fieldtype": "Column Break",
"fieldname": "social_logins",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -1947,8 +1860,10 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Social Logins",
"length": 0,
"no_copy": 0,
"options": "User Social Login",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -1961,94 +1876,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "github_userid",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Github User ID",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "github_username",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Github Username",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "frappe_userid",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Frappe User ID",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -2124,7 +1951,7 @@
"istable": 0,
"max_attachments": 5,
"menu_index": 0,
"modified": "2017-11-15 13:01:00.085916",
"modified": "2017-12-29 14:37:57.759229",
"modified_by": "Administrator",
"module": "Core",
"name": "User",

View file

@ -75,8 +75,8 @@ class User(Document):
if self.language == "Loading...":
self.language = None
if (self.name not in ["Administrator", "Guest"]) and (not self.frappe_userid):
self.frappe_userid = frappe.generate_hash(length=39)
if (self.name not in ["Administrator", "Guest"]) and (not self.get_social_login_userid("frappe")):
self.set_social_login_userid("frappe", frappe.generate_hash(length=39))
def validate_roles(self):
if self.role_profile_name:
@ -494,6 +494,25 @@ class User(Document):
if len(email_accounts) != len(set(email_accounts)):
frappe.throw(_("Email Account added multiple times"))
def get_social_login_userid(self, provider):
try:
for p in self.social_logins:
if p.provider == provider:
return p.userid
except:
return None
def set_social_login_userid(self, provider, userid, username=None):
social_logins = {
"provider": provider,
"userid": userid
}
if username:
social_logins["username"] = username
self.append("social_logins", social_logins)
@frappe.whitelist()
def get_timezones():
import pytz

View file

@ -0,0 +1,189 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-12-02 13:01:20.507112",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "provider",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Provider",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_0",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "username",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Username",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_0",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "userid",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "User ID",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-12-02 15:37:58.397062",
"modified_by": "Administrator",
"module": "Core",
"name": "User Social Login",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View file

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
from frappe.model.document import Document
class UserSocialLogin(Document):
pass

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View file

@ -0,0 +1,49 @@
# Adding Social Login Provider
This guide discusses how to add a social login provider to frappe via pull request.
### Add your provider in `SocialLoginKey.get_social_login_provider`
```
providers["Frappe"] = {
"provider_name": "Frappe",
"enable_social_login": 1,
"custom_base_url": 1,
"icon":"/assets/frappe/images/favicon.png",
"redirect_url": "/api/method/frappe.www.login.login_via_frappe",
"api_endpoint": "/api/method/frappe.integrations.oauth2.openid_profile",
"api_endpoint_args":None,
"authorize_url": "/api/method/frappe.integrations.oauth2.authorize",
"access_token_url": "/api/method/frappe.integrations.oauth2.get_token",
"auth_url_data": json.dumps({
"response_type": "code",
"scope": "openid"
})
}
```
### Add provider key in exact same type case in options of `social_login_provider` select field on `Social Login Key` DocType. e.g. `Frappe`
Once the user adds a social login provider and enables it the `Authorization Code` is sent back by the provider api server on to the redirect_url mentioned on the same server. You will have to add a whitelisted method allowing guest access in `frappe.integrations.oauth2_logins`. e.g. `login_via_office365`
There many implementations of OAuth 2.0 + OpenID Connect. Here we'll discuss two ways of accessing openid information.
#### User Creation via OpenID Profile Endpoint
example:
```
@frappe.whitelist(allow_guest=True)
def login_via_frappe(code, state):
login_via_oauth2("frappe", code, state, decoder=json.loads)
```
#### User Creation via id_token
example:
```
@frappe.whitelist(allow_guest=True)
def login_via_office365(code, state):
login_via_oauth2_id_token("office_365", code, state, decoder=json.loads)
```

View file

@ -13,3 +13,4 @@ single-type-doctype
trigger-event-on-deletion-of-grid-row
dialogs-types
using-html-templates-in-javascript
adding-social-login-provider

View file

@ -63,4 +63,20 @@ To enable these signups, you need to have **Client ID** and **Client Secret** fr
<iframe src="https://www.youtube.com/embed/bG71DxxkVjQ" class="embed-responsive-item" allowfullscreen></iframe>
</div>
---
### Office 365
1. Go to [https://portal.azure.com](https://portal.azure.com)
1. Create a new Azure Active Directory > App Registration.
1. Click on New Application Registration
1. Fill the form with:
- Application Name
- Application Type - Web app / API
- Single Sign-on URL as
**http://{{ yoursite }}/api/method/frappe.www.login.login\_via\_office365**
1. Enable Multi Tenent for the added App.
1. Go to the section **Application ID** and copy the Client ID and copy Client Secret by adding new password into Social Login Key
---
<!-- markdown -->

View file

@ -3,3 +3,4 @@ how_to_setup_oauth
using_oauth
openid_connect_and_frappe_social_login
google_gsuite
social_login_key

View file

@ -0,0 +1,26 @@
# Social Login Key
Add social login providers like Facebook, Frappe, Github, Google, Microsoft, etc and enable social login.
#### Setup Social Logins
To add Social Login Key go to
> Integrations > Authentication > Social Login Key
Social Login Key
<img class="screenshot" src="/docs/assets/img/social_login_key.png">
1. Select the Social Login Provider or select "Custom"
2. If required for provider enter "Base URL"
3. To enable check "Enable Social Login" to show Icon on login screen
4. Also add Client ID and Client Secret as per provider.
e.g. Social Login Key
- **Social Login Provider** : `Frappe`
- **Client ID** : `ABCDEFG`
- **Client Secret** : `123456`
- **Enable Social Login** : `Check`
- **Base URL** : `https://erpnext.org` (required for some providers)

View file

@ -0,0 +1,78 @@
// Copyright (c) 2017, Frappe Technologies and contributors
// For license information, please see license.txt
const fields = [
"provider_name", "base_url", "custom_base_url",
"icon", "authorize_url", "access_token_url", "redirect_url",
"api_endpoint", "api_endpoint_args", "auth_url_data"
];
frappe.ui.form.on('Social Login Key', {
refresh(frm) {
frm.trigger("setup_fields");
},
custom_base_url(frm) {
frm.trigger("setup_fields");
},
social_login_provider(frm) {
if(frm.doc.social_login_provider != "Custom") {
frappe.call({
"doc": frm.doc,
"method": "get_social_login_provider",
"args": {
"provider": frm.doc.social_login_provider
}
}).done((r) => {
const provider = r.message;
for(var field of fields) {
frm.set_value(field, provider[field]);
frm.set_df_property(field, "read_only", 1);
if (frm.doc.custom_base_url) {
frm.toggle_enable("base_url", 1);
}
}
});
} else {
frm.trigger("clear_fields");
frm.trigger("setup_fields");
}
},
setup_fields(frm) {
// set custom_base_url to read only for "Custom" provider
if(frm.doc.social_login_provider == "Custom") {
frm.set_value("custom_base_url", 1);
frm.set_df_property("custom_base_url", "read_only", 1);
}
// set fields to read only for providers from template
for(var f of fields) {
if(frm.doc.social_login_provider != "Custom"){
frm.set_df_property(f, "read_only", 1);
}
}
// enable base_url for providers with custom_base_url
if(frm.doc.custom_base_url) {
frm.set_df_property("base_url", "read_only", 0);
frm.fields_dict["sb_identity_details"].collapse(false);
}
// hide social_login_provider and provider_name for non local
if(!frm.doc.__islocal &&
(frm.doc.social_login_provider ||
frm.doc.provider_name)) {
frm.set_df_property("social_login_provider", "hidden", 1);
frm.set_df_property("provider_name", "hidden", 1);
}
},
clear_fields(frm) {
for(var field of fields){
frm.set_value(field, "");
frm.set_df_property(field, "read_only", 0);
}
}
});

View file

@ -0,0 +1,700 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"beta": 0,
"creation": "2017-11-18 15:36:09.676722",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "enable_social_login",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Enable Social Login",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:doc.enable_social_login",
"columns": 0,
"fieldname": "client_credentials",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Client Credentials",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Custom",
"depends_on": "eval:doc.custom!=1",
"fieldname": "social_login_provider",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Social Login Provider",
"length": 0,
"no_copy": 0,
"options": "Custom\nFacebook\nFrappe\nGitHub\nGoogle\nOffice 365",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 1,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "client_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Client ID",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_0",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "provider_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Provider Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 1,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "client_secret",
"fieldtype": "Password",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Client Secret",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:doc.custom_base_url",
"columns": 0,
"depends_on": "",
"fieldname": "sb_identity_details",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Identity Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "icon",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Icon",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "base_url",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Base URL",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:doc.social_login_provider===\"Custom\"",
"columns": 0,
"depends_on": "",
"fieldname": "client_urls",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Client URLs",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "authorize_url",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Authorize URL",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "access_token_url",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Access Token URL",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "redirect_url",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Redirect URL",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "api_endpoint",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "API Endpoint",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "custom_base_url",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Custom Base URL",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:doc.social_login_provider===\"Custom\"",
"columns": 0,
"depends_on": "",
"fieldname": "client_information",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Client Information",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "api_endpoint_args",
"fieldtype": "Code",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "API Endpoint Args",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "auth_url_data",
"fieldtype": "Code",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Auth URL Data",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-21 13:55:17.041059",
"modified_by": "Administrator",
"module": "Integrations",
"name": "Social Login Key",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "provider_name",
"track_changes": 1,
"track_seen": 0
}

View file

@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, json
from frappe import _
from frappe.model.document import Document
class BaseUrlNotSetError(frappe.ValidationError): pass
class AuthorizeUrlNotSetError(frappe.ValidationError): pass
class AccessTokenUrlNotSetError(frappe.ValidationError): pass
class RedirectUrlNotSetError(frappe.ValidationError): pass
class ClientIDNotSetError(frappe.ValidationError): pass
class ClientSecretNotSetError(frappe.ValidationError): pass
class SocialLoginKey(Document):
def autoname(self):
self.name = frappe.scrub(self.provider_name)
def validate(self):
if self.custom_base_url and not self.base_url:
frappe.throw(_("Please enter Base URL"), exc=BaseUrlNotSetError)
if not self.authorize_url:
frappe.throw(_("Please enter Authorize URL"), exc=AuthorizeUrlNotSetError)
if not self.access_token_url:
frappe.throw(_("Please enter Access Token URL"), exc=AccessTokenUrlNotSetError)
if not self.redirect_url:
frappe.throw(_("Please enter Redirect URL"), exc=RedirectUrlNotSetError)
if self.enable_social_login and not self.client_id:
frappe.throw(_("Please enter Client ID before social login is enabled"), exc=ClientIDNotSetError)
if self.enable_social_login and not self.client_secret:
frappe.throw(_("Please enter Client Secret before social login is enabled"), exc=ClientSecretNotSetError)
def get_social_login_provider(self, provider, initialize=False):
providers = {}
providers["Office 365"] = {
"provider_name": "Office 365",
"enable_social_login": 1,
"base_url": "https://login.microsoftonline.com",
"custom_base_url": 0,
"icon":"fa fa-windows",
"authorize_url": "https://login.microsoftonline.com/common/oauth2/authorize",
"access_token_url": "https://login.microsoftonline.com/common/oauth2/token",
"redirect_url": "/api/method/frappe.integrations.oauth2_logins.login_via_office365",
"api_endpoint": None,
"api_endpoint_args":None,
"auth_url_data": json.dumps({
"response_type": "code",
"scope":"openid"
})
}
providers["GitHub"] = {
"provider_name":"GitHub",
"enable_social_login": 1,
"base_url":"https://api.github.com/",
"custom_base_url":0,
"icon":"fa fa-github",
"authorize_url":"https://github.com/login/oauth/authorize",
"access_token_url":"https://github.com/login/oauth/access_token",
"redirect_url":"/api/method/frappe.www.login.login_via_github",
"api_endpoint":"user",
"api_endpoint_args":None,
"auth_url_data":None
}
providers["Google"] = {
"provider_name": "Google",
"enable_social_login": 1,
"base_url": "https://www.googleapis.com",
"custom_base_url": 0,
"icon":"fa fa-google",
"authorize_url": "https://accounts.google.com/o/oauth2/auth",
"access_token_url": "https://accounts.google.com/o/oauth2/token",
"redirect_url": "/api/method/frappe.www.login.login_via_google",
"api_endpoint": "oauth2/v2/userinfo",
"api_endpoint_args":None,
"auth_url_data": json.dumps({
"scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
"response_type": "code"
})
}
providers["Facebook"] = {
"provider_name": "Facebook",
"enable_social_login": 1,
"base_url": "https://graph.facebook.com",
"custom_base_url": 0,
"icon": "fa fa-facebook",
"authorize_url": "https://www.facebook.com/dialog/oauth",
"access_token_url": "https://graph.facebook.com/oauth/access_token",
"redirect_url": "/api/method/frappe.www.login.login_via_facebook",
"api_endpoint": "/v2.5/me",
"api_endpoint_args": json.dumps({
"fields": "first_name,last_name,email,gender,location,verified,picture"
}),
"auth_url_data": json.dumps({
"display": "page",
"response_type": "code",
"scope": "email,public_profile"
})
}
providers["Frappe"] = {
"provider_name": "Frappe",
"enable_social_login": 1,
"custom_base_url": 1,
"icon":"/assets/frappe/images/favicon.png",
"redirect_url": "/api/method/frappe.www.login.login_via_frappe",
"api_endpoint": "/api/method/frappe.integrations.oauth2.openid_profile",
"api_endpoint_args":None,
"authorize_url": "/api/method/frappe.integrations.oauth2.authorize",
"access_token_url": "/api/method/frappe.integrations.oauth2.get_token",
"auth_url_data": json.dumps({
"response_type": "code",
"scope": "openid"
})
}
# Initialize the doc and return, used in patch
# Or can be used for creating key from controller
if initialize and provider:
for k, v in providers[provider].items():
setattr(self,k,v)
return
return providers.get(provider) if provider else providers

View file

@ -0,0 +1,23 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Social Login Key", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Social Login Key
() => frappe.tests.make('Social Login Key', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View file

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
from frappe.integrations.doctype.social_login_key.social_login_key import BaseUrlNotSetError
import unittest
class TestSocialLoginKey(unittest.TestCase):
def test_adding_frappe_social_login_provider(self):
provider_name = "Frappe"
social_login_key = make_social_login_key(
social_login_provider=provider_name
)
social_login_key.get_social_login_provider(provider_name, initialize=True)
self.assertRaises(BaseUrlNotSetError, social_login_key.insert)
def make_social_login_key(**kwargs):
kwargs["doctype"] = "Social Login Key"
if not "provider_name" in kwargs:
kwargs["provider_name"] = "Test OAuth2 Provider"
doc = frappe.get_doc(kwargs)
return doc

View file

@ -1,8 +0,0 @@
// Copyright (c) 2016, Frappe Technologies and contributors
// For license information, please see license.txt
frappe.ui.form.on('Social Login Keys', {
refresh: function(frm) {
}
});

View file

@ -1,414 +0,0 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2014-03-04 08:29:52",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "System",
"editable_grid": 0,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "facebook",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Facebook",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "facebook_client_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Facebook Client ID",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "facebook_client_secret",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Facebook Client Secret",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "google",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Google",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "google_client_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Google Client ID",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "google_client_secret",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Google Client Secret",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "github",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "GitHub",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "github_client_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "GitHub Client ID",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "github_client_secret",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "GitHub Client Secret",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "frappe",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Frappe",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "frappe_client_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Frappe Client ID",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "frappe_client_secret",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Frappe Client Secret",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "frappe_server_url",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Frappe Server URL",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "icon-signin",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2016-12-29 14:40:30.397643",
"modified_by": "Administrator",
"module": "Integrations",
"name": "Social Login Keys",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_order": "ASC",
"track_changes": 1,
"track_seen": 0
}

View file

@ -1,35 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import requests
import socket
from frappe.model.document import Document
from frappe import _
from six.moves.urllib.parse import urlparse
class SocialLoginKeys(Document):
def validate(self):
self.validate_frappe_server_url()
def validate_frappe_server_url(self):
if self.frappe_server_url:
if self.frappe_server_url.endswith('/'):
self.frappe_server_url = self.frappe_server_url[:-1]
try:
frappe_server_hostname = urlparse(self.frappe_server_url).netloc
except:
frappe.throw(_("Check Frappe Server URL"))
if socket.gethostname() != frappe_server_hostname or \
(frappe.local.conf.domains is not None) and \
(frappe_server_hostname not in frappe.local.conf.domains):
try:
requests.get(self.frappe_server_url + "/api/method/frappe.handler.version", timeout=5)
except:
frappe.throw(_("Unable to make request to the Frappe Server URL"))

View file

@ -103,9 +103,9 @@ def get_token(*args, **kwargs):
headers = r.headers
#Check whether frappe server URL is set
frappe_server_url = frappe.db.get_value("Social Login Keys", None, "frappe_server_url") or None
frappe_server_url = frappe.db.get_value("Social Login Key", "frappe", "base_url") or None
if not frappe_server_url:
frappe.throw(_("Define Frappe Server URL in Social Login Keys"))
frappe.throw(_("Please set Base URL in Social Login Key for Frappe"))
try:
headers, body, status = get_oauth_server().create_token_response(uri, http_method, body, headers, frappe.flags.oauth_credentials)
@ -124,7 +124,7 @@ def get_token(*args, **kwargs):
id_token = {
"aud": token_client,
"exp": int((frappe.db.get_value("OAuth Bearer Token", out.access_token, "expiration_time") - frappe.utils.datetime.datetime(1970, 1, 1)).total_seconds()),
"sub": frappe.db.get_value("User", token_user, "frappe_userid"),
"sub": frappe.db.get_value("User Social Login", {"parent":token_user, "provider": "frappe"}, "userid"),
"iss": frappe_server_url,
"at_hash": frappe.oauth.calculate_at_hash(out.access_token, hashlib.sha256)
}
@ -156,7 +156,8 @@ def revoke_token(*args, **kwargs):
@frappe.whitelist()
def openid_profile(*args, **kwargs):
picture = None
first_name, last_name, avatar, name, frappe_userid = frappe.db.get_value("User", frappe.session.user, ["first_name", "last_name", "user_image", "name", "frappe_userid"])
first_name, last_name, avatar, name = frappe.db.get_value("User", frappe.session.user, ["first_name", "last_name", "user_image", "name"])
frappe_userid = frappe.db.get_value("User Social Login", {"parent":frappe.session.user, "provider": "frappe"}, "userid")
request_url = urlparse(frappe.request.url)
if avatar:

View file

@ -0,0 +1,28 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import frappe
import frappe.utils
from frappe.utils.oauth import login_via_oauth2, login_via_oauth2_id_token
import json
@frappe.whitelist(allow_guest=True)
def login_via_google(code, state):
login_via_oauth2("google", code, state, decoder=json.loads)
@frappe.whitelist(allow_guest=True)
def login_via_github(code, state):
login_via_oauth2("github", code, state)
@frappe.whitelist(allow_guest=True)
def login_via_facebook(code, state):
login_via_oauth2("facebook", code, state, decoder=json.loads)
@frappe.whitelist(allow_guest=True)
def login_via_frappe(code, state):
login_via_oauth2("frappe", code, state, decoder=json.loads)
@frappe.whitelist(allow_guest=True)
def login_via_office365(code, state):
login_via_oauth2_id_token("office_365", code, state, decoder=json.loads)

View file

@ -387,7 +387,7 @@ class OAuthWebRequestValidator(RequestValidator):
- OpenIDConnectImplicit
- OpenIDConnectHybrid
"""
if id_token_hint and id_token_hint == frappe.get_value("User", frappe.session.user, "frappe_userid"):
if id_token_hint and id_token_hint == frappe.db.get_value("User Social Login", {"parent":frappe.session.user, "provider": "frappe"}, "userid"):
return True
else:
return False

View file

@ -200,4 +200,5 @@ frappe.patches.v9_1.resave_domain_settings
frappe.patches.v9_1.revert_domain_settings
frappe.patches.v9_1.move_feed_to_activity_log
execute:frappe.delete_doc('Page', 'data-import-tool', ignore_missing=True)
frappe.patches.v10_0.reload_countries_and_currencies
frappe.patches.v10_0.reload_countries_and_currencies
frappe.patches.v10_0.refactor_social_login_keys

View file

@ -0,0 +1,91 @@
# see license
import frappe
def execute():
# Move User Data into DocType
frappe.reload_doc("core", "doctype", "user", force=True)
frappe.reload_doc("core", "doctype", "user_social_login", force=True)
users = frappe.get_all("User", filters=[["username", "not in", ["Guest","Administrator"]]])
for u in users:
user = frappe.get_doc("User", u.get("name"))
save = False
if user.fb_userid and user.fb_username:
user.append("social_logins", {
"provider": "facebook",
"userid": user.fb_userid,
"username": user.fb_username
})
save = True
if user.frappe_userid:
user.append("social_logins", {
"provider": "frappe",
"userid": user.frappe_userid
})
save = True
if user.github_userid and user.github_username:
user.append("social_logins", {
"provider": "github",
"userid": user.github_userid,
"username": user.github_username,
})
save = True
if user.google_userid:
user.append("social_logins", {
"provider": "google",
"userid": user.google_userid,
})
save = True
if save:
user.save()
# Create Social Login Key(s) from Social Login Keys
frappe.reload_doc("integrations", "doctype", "social_login_key", force=True)
social_login_keys = frappe.get_doc("Social Login Keys", "Social Login Keys")
if social_login_keys.facebook_client_id or social_login_keys.facebook_client_secret:
facebook_login_key = frappe.new_doc("Social Login Key")
facebook_login_key.get_social_login_provider("Facebook", initialize=True)
facebook_login_key.social_login_provider = "Facebook"
facebook_login_key.client_id = social_login_keys.facebook_client_id
facebook_login_key.client_secret = social_login_keys.facebook_client_secret
if not (facebook_login_key.client_secret and facebook_login_key.client_id):
facebook_login_key.enable_social_login = 0
facebook_login_key.save()
if social_login_keys.frappe_server_url:
frappe_login_key = frappe.new_doc("Social Login Key")
frappe_login_key.get_social_login_provider("Frappe", initialize=True)
frappe_login_key.social_login_provider = "Frappe"
frappe_login_key.base_url = social_login_keys.frappe_server_url
frappe_login_key.client_id = social_login_keys.frappe_client_id
frappe_login_key.client_secret = social_login_keys.frappe_client_secret
if not (frappe_login_key.client_secret and frappe_login_key.client_id and frappe_login_key.base_url):
frappe_login_key.enable_social_login = 0
frappe_login_key.save()
if social_login_keys.github_client_id or social_login_keys.github_client_secret:
github_login_key = frappe.new_doc("Social Login Key")
github_login_key.get_social_login_provider("GitHub", initialize=True)
github_login_key.social_login_provider = "GitHub"
github_login_key.client_id = social_login_keys.github_client_id
github_login_key.client_secret = social_login_keys.github_client_secret
if not (github_login_key.client_secret and github_login_key.client_id):
github_login_key.enable_social_login = 0
github_login_key.save()
if social_login_keys.google_client_id or social_login_keys.google_client_secret:
google_login_key = frappe.new_doc("Social Login Key")
google_login_key.get_social_login_provider("Google", initialize=True)
google_login_key.social_login_provider = "Google"
google_login_key.client_id = social_login_keys.google_client_id
google_login_key.client_secret = social_login_keys.google_client_secret
if not (google_login_key.client_secret and google_login_key.client_id):
google_login_key.enable_social_login = 0
google_login_key.save()
frappe.delete_doc("DocType", "Social Login Keys")

View file

@ -16,8 +16,13 @@ class TestFrappeOAuth2Client(unittest.TestCase):
self.client_id = frappe.get_all("OAuth Client", fields=["*"])[0].get("client_id")
# Set Frappe server URL reqired for id_token generation
frappe.db.set_value("Social Login Keys", None, "frappe_server_url", "http://localhost:8000")
frappe.db.commit()
try:
frappe_login_key = frappe.get_doc("Social Login Key", "frappe")
except frappe.DoesNotExistError:
frappe_login_key = frappe.new_doc("Social Login Key")
frappe_login_key.get_social_login_provider("Frappe", initialize=True)
frappe_login_key.base_url = "http://localhost:8000"
frappe_login_key.save()
def test_insert_note(self):

View file

@ -15,8 +15,13 @@ class TestOAuth20(unittest.TestCase):
self.client_id = frappe.get_all("OAuth Client", fields=["*"])[0].get("client_id")
# Set Frappe server URL reqired for id_token generation
frappe.db.set_value("Social Login Keys", None, "frappe_server_url", "http://localhost:8000")
frappe.db.commit()
try:
frappe_login_key = frappe.get_doc("Social Login Key", "frappe")
except frappe.DoesNotExistError:
frappe_login_key = frappe.new_doc("Social Login Key")
frappe_login_key.get_social_login_provider("Frappe", initialize=True)
frappe_login_key.base_url = "http://localhost:8000"
frappe_login_key.save()
def test_login_using_authorization_code(self):
@ -113,3 +118,6 @@ class TestOAuth20(unittest.TestCase):
self.assertTrue(response_url.get("expires_in"))
self.assertTrue(response_url.get("scope"))
self.assertTrue(response_url.get("token_type"))
def tearDown(self):
self.driver.close()

View file

@ -0,0 +1,33 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import unittest, frappe, time
from frappe.utils.selenium_testdriver import TestDriver
class TestSocialLoginKeyButtons(unittest.TestCase):
def setUp(self):
try:
frappe_login_key = frappe.get_doc("Social Login Key", "frappe")
except frappe.DoesNotExistError:
frappe_login_key = frappe.new_doc("Social Login Key")
frappe_login_key.get_social_login_provider("Frappe", initialize=True)
frappe_login_key.base_url = "http://localhost:8000"
frappe_login_key.enable_social_login = 1
frappe_login_key.client_id = "test_client_id"
frappe_login_key.client_secret = "test_client_secret"
frappe_login_key.save()
self.driver = TestDriver()
def test_login_buttons(self):
# Go to Login Page
self.driver.get("login")
time.sleep(2)
frappe_social_login = self.driver.find(".btn-frappe")
self.assertTrue(len(frappe_social_login) > 0)
def tearDown(self):
self.driver.close()

View file

@ -1,4 +1,4 @@
import json
import json, re
import bleach, bleach_whitelist.bleach_whitelist as bleach_whitelist
from six import string_types
@ -48,6 +48,24 @@ def is_json(text):
else:
return True
def get_icon_html(icon, small=False):
emoji_pattern = re.compile(u'['
u'\U0001F300-\U0001F64F'
u'\U0001F680-\U0001F6FF'
u'\u2600-\u26FF\u2700-\u27BF]+',
re.UNICODE)
if icon and emoji_pattern.match(icon):
return '<span class="text-muted">' + icon + '</span>'
if frappe.utils.is_image(icon):
return \
'<img style="width: 16px; height: 16px;" src="{icon}">'.format(icon=icon) \
if small else \
'<img src="{icon}">'.format(icon=icon)
else:
return "<i class='{icon}'></i>".format(icon=icon)
# adapted from https://raw.githubusercontent.com/html5lib/html5lib-python/4aa79f113e7486c7ec5d15a6e1777bfe546d3259/html5lib/sanitizer.py
acceptable_elements = [
'a', 'abbr', 'acronym', 'address', 'area',
@ -149,4 +167,4 @@ svg_attributes = [
'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type',
'xml:base', 'xml:lang', 'xml:space', 'xmlns', 'xmlns:xlink', 'y',
'y1', 'y2', 'zoomAndPan'
]
]

View file

@ -4,91 +4,36 @@
from __future__ import unicode_literals
import frappe
import frappe.utils
import json
import json, jwt
from frappe import _
from frappe.utils.password import get_decrypted_password
from six import string_types
class SignupDisabledError(frappe.PermissionError): pass
def get_oauth2_providers():
out = {
"google": {
out = {}
providers = frappe.get_all("Social Login Key", fields=["*"])
for provider in providers:
authorize_url, access_token_url = provider.authorize_url, provider.access_token_url
if provider.custom_base_url:
authorize_url = provider.base_url + provider.authorize_url
access_token_url = provider.base_url + provider.access_token_url
out[provider.name] = {
"flow_params": {
"name": "google",
"authorize_url": "https://accounts.google.com/o/oauth2/auth",
"access_token_url": "https://accounts.google.com/o/oauth2/token",
"base_url": "https://www.googleapis.com",
},
"redirect_uri": "/api/method/frappe.www.login.login_via_google",
"auth_url_data": {
"scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
"response_type": "code"
},
# relative to base_url
"api_endpoint": "oauth2/v2/userinfo"
},
"github": {
"flow_params": {
"name": "github",
"authorize_url": "https://github.com/login/oauth/authorize",
"access_token_url": "https://github.com/login/oauth/access_token",
"base_url": "https://api.github.com/"
},
"redirect_uri": "/api/method/frappe.www.login.login_via_github",
# relative to base_url
"api_endpoint": "user"
},
"facebook": {
"flow_params": {
"name": "facebook",
"authorize_url": "https://www.facebook.com/dialog/oauth",
"access_token_url": "https://graph.facebook.com/oauth/access_token",
"base_url": "https://graph.facebook.com"
},
"redirect_uri": "/api/method/frappe.www.login.login_via_facebook",
"auth_url_data": {
"display": "page",
"response_type": "code",
"scope": "email,public_profile"
},
# relative to base_url
"api_endpoint": "/v2.5/me",
"api_endpoint_args": {
"fields": "first_name,last_name,email,gender,location,verified,picture"
"name": provider.name,
"authorize_url": authorize_url,
"access_token_url": access_token_url,
"base_url": provider.base_url
},
"redirect_uri": provider.redirect_url,
"api_endpoint": provider.api_endpoint,
}
}
if provider.auth_url_data:
out[provider.name]["auth_url_data"] = json.loads(provider.auth_url_data)
frappe_server_url = frappe.db.get_value("Social Login Keys", None, "frappe_server_url")
if frappe_server_url:
out['frappe'] = {
"flow_params": {
"name": "frappe",
"authorize_url": frappe_server_url + "/api/method/frappe.integrations.oauth2.authorize",
"access_token_url": frappe_server_url + "/api/method/frappe.integrations.oauth2.get_token",
"base_url": frappe_server_url
},
"redirect_uri": "/api/method/frappe.www.login.login_via_frappe",
"auth_url_data": {
"response_type": "code",
"scope": "openid"
},
# relative to base_url
"api_endpoint": "/api/method/frappe.integrations.oauth2.openid_profile"
}
if provider.api_endpoint_args:
out[provider.name]["api_endpoint_args"] = json.loads(provider.api_endpoint_args)
return out
@ -100,17 +45,13 @@ def get_oauth_keys(provider):
if not keys:
# try database
social = frappe.get_doc("Social Login Keys", "Social Login Keys")
keys = {}
for fieldname in ("client_id", "client_secret"):
value = social.get("{provider}_{fieldname}".format(provider=provider, fieldname=fieldname))
if not value:
keys = {}
break
keys[fieldname] = value
client_id, client_secret = frappe.get_value("Social Login Key", provider, ["client_id", "client_secret"])
client_secret = get_decrypted_password("Social Login Key", provider, "client_secret")
keys = {
"client_id": client_id,
"client_secret": client_secret
}
return keys
else:
return {
"client_id": keys["client_id"],
@ -169,7 +110,11 @@ def login_via_oauth2(provider, code, state, decoder=None):
info = get_info_via_oauth(provider, code, decoder)
login_oauth_user(info, provider=provider, state=state)
def get_info_via_oauth(provider, code, decoder=None):
def login_via_oauth2_id_token(provider, code, state, decoder=None):
info = get_info_via_oauth(provider, code, decoder, id_token=True)
login_oauth_user(info, provider=provider, state=state)
def get_info_via_oauth(provider, code, decoder=None, id_token=False):
flow = get_oauth2_flow(provider)
oauth2_providers = get_oauth2_providers()
@ -186,9 +131,14 @@ def get_info_via_oauth(provider, code, decoder=None):
session = flow.get_auth_session(**args)
api_endpoint = oauth2_providers[provider].get("api_endpoint")
api_endpoint_args = oauth2_providers[provider].get("api_endpoint_args")
info = session.get(api_endpoint, params=api_endpoint_args).json()
if id_token:
parsed_access = json.loads(session.access_token_response.text)
token = parsed_access['id_token']
info = jwt.decode(token, flow.client_secret, verify=False)
else:
api_endpoint = oauth2_providers[provider].get("api_endpoint")
api_endpoint_args = oauth2_providers[provider].get("api_endpoint_args")
info = session.get(api_endpoint, params=api_endpoint_args).json()
if (("verified_email" in info and not info.get("verified_email"))
or ("verified" in info and not info.get("verified"))):
@ -289,26 +239,28 @@ def update_oauth_user(user, data, provider):
frappe.respond_as_web_page(_('Not Allowed'), _('User {0} is disabled').format(user.email))
return False
if provider=="facebook" and not user.get("fb_userid"):
if provider=="facebook" and not user.get_social_login_userid(provider):
save = True
user.set_social_login_userid(provider, userid=data["id"], username=data.get("username"))
user.update({
"fb_username": data.get("username"),
"fb_userid": data["id"],
"user_image": "https://graph.facebook.com/{id}/picture".format(id=data["id"])
})
elif provider=="google" and not user.get("google_userid"):
elif provider=="google" and not user.get_social_login_userid(provider):
save = True
user.google_userid = data["id"]
user.set_social_login_userid(provider, userid=data["id"])
elif provider=="github" and not user.get("github_userid"):
elif provider=="github" and not user.get_social_login_userid(provider):
save = True
user.github_userid = data["id"]
user.github_username = data["login"]
user.set_social_login_userid(provider, userid=data["id"], username=data.get("login"))
elif provider=="frappe" and not user.get("frappe_userid"):
elif provider=="frappe" and not user.get_social_login_userid(provider):
save = True
user.frappe_userid = data["sub"]
user.set_social_login_userid(provider, userid=data["sub"])
elif provider=="office_365" and not user.get_social_login_userid(provider):
save = True
user.set_social_login_userid(provider, userid=data["sub"])
if save:
user.flags.ignore_permissions = True

View file

@ -3,7 +3,11 @@
from __future__ import unicode_literals
from zxcvbn import zxcvbn
try:
from zxcvbn import zxcvbn
except Exception as e:
import zxcvbn
import frappe
from frappe import _

View file

@ -39,27 +39,11 @@
<h6>{{ _("Or login with") }}</h6>
<p class="text-center" style="margin-top: 15px">
{%- if facebook_login is defined %}
<a href="{{ facebook_login }}"
class="btn btn-default btn-xs btn-social btn-facebook">
<i class="fa fa-facebook-official"></i> {{ _("Facebook") }}</a>
{%- endif -%}
{%- if google_login is defined %}
<a href="{{ google_login }}" class="btn btn-default btn-xs btn-social btn-google">
<i class="fa fa-google-plus"></i> {{ _("Google") }}</a>
{%- endif -%}
{%- if github_login is defined %}
<a href="{{ github_login }}" class="btn btn-default btn-xs btn-social btn-github">
<i class="fa fa-github"></i> {{ _("GitHub") }}</a>
{%- endif -%}
{%- if frappe_login is defined %}
<a href="{{ frappe_login }}" class="btn btn-default btn-xs btn-social">
<img style='width: 16px; height: 16px;'
src="/assets/frappe/images/favicon.png"> {{ _("Frappe") }}</a>
{%- endif -%}
{% for provider in provider_logins %}
<a href="{{ provider.auth_url }}"
class="btn btn-default btn-xs btn-social btn-{{ provider.name }}">
{{ provider.icon }} {{ provider.provider_name }}</a>
{% endfor %}
</p>
{%- endif -%}
</div>

View file

@ -4,11 +4,13 @@
from __future__ import unicode_literals
import frappe
import frappe.utils
from frappe.utils.oauth import get_oauth2_authorize_url, get_oauth_keys, login_via_oauth2, login_oauth_user as _login_oauth_user, redirect_post_login
from frappe.utils.oauth import get_oauth2_authorize_url, get_oauth_keys, login_via_oauth2, login_via_oauth2_id_token, login_oauth_user as _login_oauth_user, redirect_post_login
import json
from frappe import _
from frappe.auth import LoginManager
from frappe.integrations.doctype.ldap_settings.ldap_settings import get_ldap_settings
from frappe.utils.password import get_decrypted_password
from frappe.utils.html_utils import get_icon_html
no_cache = True
@ -21,11 +23,20 @@ def get_context(context):
context.no_header = True
context.for_test = 'login.html'
context["title"] = "Login"
context["provider_logins"] = []
context["disable_signup"] = frappe.utils.cint(frappe.db.get_value("Website Settings", "Website Settings", "disable_signup"))
for provider in ("google", "github", "facebook", "frappe"):
if get_oauth_keys(provider):
context["{provider}_login".format(provider=provider)] = get_oauth2_authorize_url(provider)
providers = [i.name for i in frappe.get_all("Social Login Key", filters={"enable_social_login":1})]
for provider in providers:
client_id, base_url = frappe.get_value("Social Login Key", provider, ["client_id", "base_url"])
client_secret = get_decrypted_password("Social Login Key", provider, "client_secret")
icon = get_icon_html(frappe.get_value("Social Login Key", provider, "icon"), small=True)
if (get_oauth_keys(provider) and client_secret and client_id and base_url):
context.provider_logins.append({
"name": provider,
"provider_name": frappe.get_value("Social Login Key", provider, "provider_name"),
"auth_url": get_oauth2_authorize_url(provider),
"icon": icon
})
context["social_login"] = True
ldap_settings = get_ldap_settings()
@ -59,6 +70,10 @@ def login_via_facebook(code, state):
def login_via_frappe(code, state):
login_via_oauth2("frappe", code, state, decoder=json.loads)
@frappe.whitelist(allow_guest=True)
def login_via_office365(code, state):
login_via_oauth2_id_token("office_365", code, state, decoder=json.loads)
@frappe.whitelist(allow_guest=True)
def login_oauth_user(data=None, provider=None, state=None, email_id=None, key=None, generate_login_token=False):
if not ((data and provider and state) or (email_id and key)):