Finishing touches to Google OAuthversion-14
@@ -119,14 +119,14 @@ def init(site, sites_path=None): | |||||
def get_site_config(): | def get_site_config(): | ||||
config = {} | config = {} | ||||
sites_config_filepath = os.path.join(local.sites_path, "site_config.json") | |||||
common_site_config_filepath = os.path.join(local.sites_path, "common_site_config.json") | |||||
site_config_filepath = os.path.join(local.site_path, "site_config.json") | site_config_filepath = os.path.join(local.site_path, "site_config.json") | ||||
if os.path.exists(sites_config_filepath): | |||||
config = get_file_json(sites_config_filepath) | |||||
if os.path.exists(common_site_config_filepath): | |||||
config = get_file_json(common_site_config_filepath) | |||||
if os.path.exists(site_config_filepath): | if os.path.exists(site_config_filepath): | ||||
config.update(get_file_json(site_config_filepath)) | config.update(get_file_json(site_config_filepath)) | ||||
return _dict(config) | return _dict(config) | ||||
def destroy(): | def destroy(): | ||||
"""closes connection and releases werkzeug local""" | """closes connection and releases werkzeug local""" | ||||
if db: | if db: | ||||
@@ -10,14 +10,12 @@ Build the `public` folders and setup languages | |||||
import os, sys, frappe, json, shutil | import os, sys, frappe, json, shutil | ||||
from cssmin import cssmin | from cssmin import cssmin | ||||
import frappe.translate | |||||
def bundle(no_compress): | def bundle(no_compress): | ||||
"""concat / minify js files""" | """concat / minify js files""" | ||||
# build js files | # build js files | ||||
make_asset_dirs() | make_asset_dirs() | ||||
build(no_compress) | build(no_compress) | ||||
frappe.translate.clear_cache() | |||||
def watch(no_compress): | def watch(no_compress): | ||||
"""watch and rebuild if necessary""" | """watch and rebuild if necessary""" | ||||
@@ -32,7 +30,6 @@ def watch(no_compress): | |||||
def make_asset_dirs(): | def make_asset_dirs(): | ||||
assets_path = os.path.join(frappe.local.sites_path, "assets") | assets_path = os.path.join(frappe.local.sites_path, "assets") | ||||
site_public_path = os.path.join(frappe.local.site_path, 'public') | |||||
for dir_path in [ | for dir_path in [ | ||||
os.path.join(assets_path, 'js'), | os.path.join(assets_path, 'js'), | ||||
os.path.join(assets_path, 'css')]: | os.path.join(assets_path, 'css')]: | ||||
@@ -8,7 +8,7 @@ import sys, os | |||||
import frappe | import frappe | ||||
site_arg_optional = ['serve'] | |||||
site_arg_optional = ['serve', 'build', 'watch'] | |||||
def get_site(parsed_args): | def get_site(parsed_args): | ||||
if not parsed_args.get("site") and os.path.exists(os.path.join(parsed_args["sites_path"], "currentsite.txt")): | if not parsed_args.get("site") and os.path.exists(os.path.join(parsed_args["sites_path"], "currentsite.txt")): | ||||
@@ -41,6 +41,9 @@ def main(): | |||||
exit(1) | exit(1) | ||||
elif site: | elif site: | ||||
frappe.init(site, sites_path=sites_path) | frappe.init(site, sites_path=sites_path) | ||||
else: | |||||
# site argument optional | |||||
frappe.init("", sites_path=sites_path) | |||||
run(fn, parsed_args) | run(fn, parsed_args) | ||||
else: | else: | ||||
run(fn, parsed_args) | run(fn, parsed_args) | ||||
@@ -298,6 +301,7 @@ def latest(verbose=True, rebuild_website_config=True): | |||||
import frappe.model.sync | import frappe.model.sync | ||||
from frappe.website import rebuild_config | from frappe.website import rebuild_config | ||||
from frappe.utils.fixtures import sync_fixtures | from frappe.utils.fixtures import sync_fixtures | ||||
import frappe.translate | |||||
frappe.connect() | frappe.connect() | ||||
@@ -317,6 +321,8 @@ def latest(verbose=True, rebuild_website_config=True): | |||||
sync_fixtures() | sync_fixtures() | ||||
frappe.translate.clear_cache() | |||||
except frappe.modules.patch_handler.PatchError, e: | except frappe.modules.patch_handler.PatchError, e: | ||||
print "\n".join(frappe.local.patch_log_list) | print "\n".join(frappe.local.patch_log_list) | ||||
raise | raise | ||||
@@ -357,53 +357,6 @@ def reset_password(user): | |||||
return "Password reset details sent to your email." | return "Password reset details sent to your email." | ||||
else: | else: | ||||
return "No such user (%s)" % user | return "No such user (%s)" % user | ||||
@frappe.whitelist(allow_guest=True) | |||||
def facebook_login(data): | |||||
data = json.loads(data) | |||||
if not (data.get("id") and data.get("fb_access_token")): | |||||
raise frappe.ValidationError | |||||
user = data["email"] | |||||
if not get_fb_userid(data.get("fb_access_token")): | |||||
# garbage | |||||
raise frappe.ValidationError | |||||
if not frappe.db.exists("Profile", user): | |||||
if data.get("birthday"): | |||||
b = data.get("birthday").split("/") | |||||
data["birthday"] = b[2] + "-" + b[0] + "-" + b[1] | |||||
profile = frappe.bean({ | |||||
"doctype":"Profile", | |||||
"first_name": data["first_name"], | |||||
"last_name": data["last_name"], | |||||
"email": data["email"], | |||||
"enabled": 1, | |||||
"new_password": frappe.generate_hash(data["email"]), | |||||
"fb_username": data["username"], | |||||
"fb_userid": data["id"], | |||||
"location": data.get("location", {}).get("name"), | |||||
"birth_date": data.get("birthday"), | |||||
"user_type": "Website User" | |||||
}) | |||||
profile.ignore_permissions = True | |||||
profile.get_controller().no_welcome_mail = True | |||||
profile.insert() | |||||
frappe.local.login_manager.user = user | |||||
frappe.local.login_manager.post_login() | |||||
def get_fb_userid(fb_access_token): | |||||
import requests | |||||
response = requests.get("https://graph.facebook.com/me?access_token=" + fb_access_token) | |||||
if response.status_code==200: | |||||
print response.json() | |||||
return response.json().get("id") | |||||
else: | |||||
return frappe.AuthenticationError | |||||
def profile_query(doctype, txt, searchfield, start, page_len, filters): | def profile_query(doctype, txt, searchfield, start, page_len, filters): | ||||
from frappe.widgets.reportview import get_match_cond | from frappe.widgets.reportview import get_match_cond | ||||
@@ -2,7 +2,7 @@ | |||||
{ | { | ||||
"creation": "2013-03-07 11:54:44", | "creation": "2013-03-07 11:54:44", | ||||
"docstatus": 0, | "docstatus": 0, | ||||
"modified": "2014-01-29 16:52:01", | |||||
"modified": "2014-02-26 17:40:31", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"owner": "Administrator" | "owner": "Administrator" | ||||
}, | }, | ||||
@@ -34,6 +34,7 @@ | |||||
}, | }, | ||||
{ | { | ||||
"cancel": 0, | "cancel": 0, | ||||
"delete": 0, | |||||
"doctype": "DocPerm", | "doctype": "DocPerm", | ||||
"name": "__common__", | "name": "__common__", | ||||
"parent": "Profile", | "parent": "Profile", | ||||
@@ -454,9 +455,9 @@ | |||||
}, | }, | ||||
{ | { | ||||
"doctype": "DocField", | "doctype": "DocField", | ||||
"fieldname": "facebook_authentication", | |||||
"fieldname": "third_party_authentication", | |||||
"fieldtype": "Section Break", | "fieldtype": "Section Break", | ||||
"label": "Facebook Authentication", | |||||
"label": "Third Party Authentication", | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | }, | ||||
{ | { | ||||
@@ -475,9 +476,16 @@ | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"read_only": 1 | "read_only": 1 | ||||
}, | }, | ||||
{ | |||||
"doctype": "DocField", | |||||
"fieldname": "google_userid", | |||||
"fieldtype": "Data", | |||||
"label": "Google User ID", | |||||
"permlevel": 0, | |||||
"read_only": 1 | |||||
}, | |||||
{ | { | ||||
"create": 1, | "create": 1, | ||||
"delete": 1, | |||||
"doctype": "DocPerm", | "doctype": "DocPerm", | ||||
"email": 1, | "email": 1, | ||||
"permlevel": 0, | "permlevel": 0, | ||||
@@ -489,7 +497,6 @@ | |||||
}, | }, | ||||
{ | { | ||||
"create": 0, | "create": 0, | ||||
"delete": 0, | |||||
"doctype": "DocPerm", | "doctype": "DocPerm", | ||||
"email": 1, | "email": 1, | ||||
"permlevel": 0, | "permlevel": 0, | ||||
@@ -497,23 +504,9 @@ | |||||
"role": "All", | "role": "All", | ||||
"write": 0 | "write": 0 | ||||
}, | }, | ||||
{ | |||||
"create": 0, | |||||
"delete": 0, | |||||
"doctype": "DocPerm", | |||||
"email": 1, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"report": 1, | |||||
"restricted": 1, | |||||
"role": "All", | |||||
"submit": 0, | |||||
"write": 0 | |||||
}, | |||||
{ | { | ||||
"amend": 0, | "amend": 0, | ||||
"create": 0, | "create": 0, | ||||
"delete": 0, | |||||
"doctype": "DocPerm", | "doctype": "DocPerm", | ||||
"permlevel": 1, | "permlevel": 1, | ||||
"report": 1, | "report": 1, | ||||
@@ -79,7 +79,7 @@ def handle(): | |||||
else: | else: | ||||
if frappe.local.request.method in ("POST", "PUT") and frappe.db: | if frappe.local.request.method in ("POST", "PUT") and frappe.db: | ||||
frappe.db.commit() | frappe.db.commit() | ||||
build_response() | build_response() | ||||
if frappe.db: | if frappe.db: | ||||
@@ -145,7 +145,7 @@ login.via_facebook = function() { | |||||
url:"/", | url:"/", | ||||
type: "POST", | type: "POST", | ||||
data: { | data: { | ||||
cmd:"frappe.core.doctype.profile.profile.facebook_login", | |||||
cmd:"frappe.templates.pages.login.login_via_facebook", | |||||
data: JSON.stringify(response) | data: JSON.stringify(response) | ||||
}, | }, | ||||
statusCode: login.login_handlers | statusCode: login.login_handlers | ||||
@@ -174,15 +174,3 @@ frappe.ready(function() { | |||||
}); | }); | ||||
{%- endif %} | {%- endif %} | ||||
{% if google_sign_in is defined -%} | |||||
frappe.ready(function() { | |||||
$(".btn-google").click(function() { | |||||
frappe.call({ | |||||
method: "frappe.templates.pages.login.get_google_auth_url" | |||||
}) | |||||
}) | |||||
}) | |||||
{%- endif -%} |
@@ -23,8 +23,7 @@ | |||||
{%- if google_sign_in is defined %} | {%- if google_sign_in is defined %} | ||||
<p class="text-center"> | <p class="text-center"> | ||||
<p class="text-muted text-center" style="margin: 10px;">or</p> | <p class="text-muted text-center" style="margin: 10px;">or</p> | ||||
<a href="{{ google_sign_in }}" target="_blank" | |||||
type="button" class="btn btn-lg btn-primary btn-block btn-google"> | |||||
<a href="{{ google_sign_in }}" type="button" class="btn btn-lg btn-primary btn-block btn-google"> | |||||
{{ _("Login via Google") }}</a></p> | {{ _("Login via Google") }}</a></p> | ||||
{%- endif -%} | {%- endif -%} | ||||
<p class="text-center"> | <p class="text-center"> | ||||
@@ -4,6 +4,8 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe, os | import frappe, os | ||||
import httplib2 | import httplib2 | ||||
import json | |||||
from werkzeug.utils import redirect | |||||
no_cache = True | no_cache = True | ||||
@@ -19,30 +21,99 @@ def get_context(context): | |||||
return context | return context | ||||
def get_google_auth_url(): | def get_google_auth_url(): | ||||
from oauth2client.client import flow_from_clientsecrets | |||||
flow = flow_from_clientsecrets(frappe.get_site_path("google_config.json"), | |||||
scope=['https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email'], | |||||
redirect_uri='http://localhost:8000/api/method/frappe.templates.pages.login.login_via_google') | |||||
flow = get_google_auth_flow() | |||||
return flow.step1_get_authorize_url() | return flow.step1_get_authorize_url() | ||||
@frappe.whitelist(allow_guest=True) | |||||
def login_via_google(code): | |||||
def get_google_auth_flow(): | |||||
from oauth2client.client import flow_from_clientsecrets | from oauth2client.client import flow_from_clientsecrets | ||||
google_config_path = frappe.get_site_path("google_config.json") | |||||
google_config = frappe.get_file_json(google_config_path) | |||||
flow = flow_from_clientsecrets(frappe.get_site_path("google_config.json"), | |||||
flow = flow_from_clientsecrets(google_config_path, | |||||
scope=['https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email'], | scope=['https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email'], | ||||
redirect_uri='http://localhost:8000/api/method/frappe.templates.pages.login.login_via_google') | |||||
redirect_uri=google_config.get("web").get("redirect_uris")[0]) | |||||
return flow | |||||
@frappe.whitelist(allow_guest=True) | |||||
def login_via_google(code): | |||||
flow = get_google_auth_flow() | |||||
credentials = flow.step2_exchange(code) | credentials = flow.step2_exchange(code) | ||||
http = httplib2.Http() | http = httplib2.Http() | ||||
http = credentials.authorize(http) | http = credentials.authorize(http) | ||||
resp, content = http.request('https://www.googleapis.com/oauth2/v2/userinfo', 'GET') | resp, content = http.request('https://www.googleapis.com/oauth2/v2/userinfo', 'GET') | ||||
info = json.loads(content) | |||||
print content | |||||
if not info.get("verified_email"): | |||||
frappe.throw("You need to verify your email with Google before you can proceed.") | |||||
frappe.local._response = redirect("/") | |||||
login_oauth_user(info, oauth_provider="google") | |||||
# because of a GET request! | |||||
frappe.db.commit() | |||||
@frappe.whitelist(allow_guest=True) | |||||
def login_via_facebook(data): | |||||
data = json.loads(data) | |||||
if not (data.get("id") and data.get("fb_access_token")): | |||||
raise frappe.ValidationError | |||||
if not get_fb_userid(data.get("fb_access_token")): | |||||
# garbage | |||||
raise frappe.ValidationError | |||||
login_oauth_user(data, oauth_provider="facebook") | |||||
def login_oauth_user(data, oauth_provider=None): | |||||
user = data["email"] | |||||
if not frappe.db.exists("Profile", user): | |||||
create_oauth_user(data, oauth_provider) | |||||
frappe.local.login_manager.user = user | |||||
frappe.local.login_manager.post_login() | |||||
def create_oauth_user(data, oauth_provider): | |||||
if data.get("birthday"): | |||||
b = data.get("birthday").split("/") | |||||
data["birthday"] = b[2] + "-" + b[0] + "-" + b[1] | |||||
profile = frappe.bean({ | |||||
"doctype":"Profile", | |||||
"first_name": data.get("first_name") or data.get("given_name"), | |||||
"last_name": data.get("last_name") or data.get("family_name"), | |||||
"email": data["email"], | |||||
"gender": data.get("gender"), | |||||
"enabled": 1, | |||||
"new_password": frappe.generate_hash(data["email"]), | |||||
"location": data.get("location", {}).get("name"), | |||||
"birth_date": data.get("birthday"), | |||||
"user_type": "Website User", | |||||
"user_image": data.get("picture") | |||||
}) | |||||
if oauth_provider=="facebook": | |||||
profile.doc.fields.update({ | |||||
"fb_username": data["username"], | |||||
"fb_userid": data["id"] | |||||
}) | |||||
elif oauth_provider=="google": | |||||
profile.doc.google_userid = data["id"] | |||||
profile.ignore_permissions = True | |||||
profile.get_controller().no_welcome_mail = True | |||||
profile.insert() | |||||
def get_fb_userid(fb_access_token): | |||||
import requests | |||||
response = requests.get("https://graph.facebook.com/me?access_token=" + fb_access_token) | |||||
if response.status_code==200: | |||||
print response.json() | |||||
return response.json().get("id") | |||||
else: | |||||
return frappe.AuthenticationError |
@@ -1,6 +1,7 @@ | |||||
chardet | chardet | ||||
cssmin | cssmin | ||||
dropbox | dropbox | ||||
rauth | |||||
oauth2client | oauth2client | ||||
gunicorn | gunicorn | ||||
httplib2 | httplib2 | ||||