From 18732a95327a2c88d0aaa182d66c231ace591f84 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 11 Jun 2015 10:01:36 -0300 Subject: [PATCH] Updated Login Module. - OAUTH LOGIN: is generic based on a new OAUTH_20_GENERIC_API - Storage (at the moment only SQL) for OAUTH_CONSUMER configuration. - OAUTH login and callback are generic. - Added a OAUTH_20_GENERIC_API. - Added scripts and templates to build the new OAUTH tables. - Fixed CMS_STORAGE_SQL_I.check_sql_query_validity issue. - Extended CMS_STORAGE_SQL_I, to execute scripts with paramerters. - Updated filter, now it's generic for every OAUTH consumer. --- .../demo/site/scripts/oauth2_consumers.sql | 20 ++ .../scripts/oauth2_consumers_initialize.sql | 11 + .../{oauth2_gmail.sql => oauth2_template.sql} | 4 +- .../modules/login/templates/block_login.tpl | 6 +- examples/demo/site/themes/bootstrap/page.tpl | 2 +- modules/login/cms_oauth_consumer.e | 152 ++++++++++++ modules/login/cms_user_oauth_api.e | 52 +++- .../{oauth_gmail_filter.e => oauth_filter.e} | 14 +- modules/login/login_constants.e | 4 +- modules/login/login_module.e | 225 +++++++++++++++--- .../{oauth_login_gmail.e => oauth_login.e} | 79 ++---- .../persistence/cms_oauth_20_generic_api.e | 94 ++++++++ .../persistence/cms_user_oauth_storage_i.e | 26 +- .../persistence/cms_user_oauth_storage_null.e | 25 +- .../persistence/cms_user_oauth_storage_sql.e | 94 ++++++-- src/persistence/sql/cms_storage_sql_i.e | 34 ++- 16 files changed, 688 insertions(+), 154 deletions(-) create mode 100644 examples/demo/site/scripts/oauth2_consumers.sql create mode 100644 examples/demo/site/scripts/oauth2_consumers_initialize.sql rename examples/demo/site/scripts/{oauth2_gmail.sql => oauth2_template.sql} (71%) create mode 100644 modules/login/cms_oauth_consumer.e rename modules/login/filter/{oauth_gmail_filter.e => oauth_filter.e} (66%) rename modules/login/{oauth_login_gmail.e => oauth_login.e} (61%) create mode 100644 modules/login/persistence/cms_oauth_20_generic_api.e diff --git a/examples/demo/site/scripts/oauth2_consumers.sql b/examples/demo/site/scripts/oauth2_consumers.sql new file mode 100644 index 0000000..6bf79d6 --- /dev/null +++ b/examples/demo/site/scripts/oauth2_consumers.sql @@ -0,0 +1,20 @@ +BEGIN; + +CREATE TABLE "oauth2_consumers"( + "cid" INTEGER PRIMARY KEY NOT NULL CHECK("cid">=0), + "name" VARCHAR(255) NOT NULL, + "api_secret" TEXT NOT NULL, + "api_key" TEXT NOT NULL, + "scope" VARCHAR (100) NOT NULL, + "protected_resource_url" VARCHAR (255) NOT NULL, + "callback_name" VARCHAR(255) NOT NULL, + "extractor" VARCHAR(50) NOT NULL, + "authorize_url" VARCHAR (255) NOT NULL, + "endpoint" VARCHAR (255) NOT NULL, + CONSTRAINT "cid" + UNIQUE("cid"), + CONSTRAINT "name" + UNIQUE("name") + ); + +COMMIT; \ No newline at end of file diff --git a/examples/demo/site/scripts/oauth2_consumers_initialize.sql b/examples/demo/site/scripts/oauth2_consumers_initialize.sql new file mode 100644 index 0000000..16766a7 --- /dev/null +++ b/examples/demo/site/scripts/oauth2_consumers_initialize.sql @@ -0,0 +1,11 @@ +BEGIN; + + -- Change the values `TO_COMPLETE' based on your API. + -- API SECTET KEY AND API PUBLIC KEY + +INSERT INTO "oauth2_consumers" ("name", "api_secret", "api_key", "scope", "protected_resource_url", "callback_name", "extractor", "authorize_url", "endpoint") +VALUES ("google", 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://www.googleapis.com/plus/v1/people/me', "callback_google", "json","https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI","https://accounts.google.com/o/oauth2/token"); +INSERT INTO "oauth2_consumers" ("name", "api_secret", "api_key", "scope", "protected_resource_url", "callback_name", "extractor", "authorize_url", "endpoint" ) +VALUES ("facebook", 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://graph.facebook.com/me', "callback_facebook","text","https://www.facebook.com/dialog/oauth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI","https://graph.facebook.com/oauth/access_token"); + +COMMIT; \ No newline at end of file diff --git a/examples/demo/site/scripts/oauth2_gmail.sql b/examples/demo/site/scripts/oauth2_template.sql similarity index 71% rename from examples/demo/site/scripts/oauth2_gmail.sql rename to examples/demo/site/scripts/oauth2_template.sql index 3efbe55..6c1fa68 100644 --- a/examples/demo/site/scripts/oauth2_gmail.sql +++ b/examples/demo/site/scripts/oauth2_template.sql @@ -1,9 +1,9 @@ BEGIN; -CREATE TABLE "oauth2_gmail"( +CREATE TABLE :table_name ( "uid" INTEGER PRIMARY KEY NOT NULL CHECK("uid">=0), - "access_token" VARCHAR(255) NOT NULL, + "access_token" TEXT NOT NULL, "created" DATETIME NOT NULL, "details" TEXT NOT NULL, CONSTRAINT "uid" diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl index 130aa76..dda41b5 100644 --- a/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl @@ -1,4 +1,4 @@ -
+
{unless isset="$user"}

Login or Register

@@ -26,7 +26,9 @@
- + {foreach item="item" from="$oauth_consumers"} + Login with {$item/}
+ {/foreach}
{/unless}
\ No newline at end of file diff --git a/examples/demo/site/themes/bootstrap/page.tpl b/examples/demo/site/themes/bootstrap/page.tpl index 9bf0155..a587c0f 100644 --- a/examples/demo/site/themes/bootstrap/page.tpl +++ b/examples/demo/site/themes/bootstrap/page.tpl @@ -57,7 +57,7 @@ {unless isempty="$page_title"}

{$page_title/}

{/unless} {$page.region_content/} - + diff --git a/modules/login/cms_oauth_consumer.e b/modules/login/cms_oauth_consumer.e new file mode 100644 index 0000000..fda8210 --- /dev/null +++ b/modules/login/cms_oauth_consumer.e @@ -0,0 +1,152 @@ +note + description: "Summary description for {CMS_OAUTH_CONSUMER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_OAUTH_CONSUMER + +inherit + + ANY + redefine + default_create + end + +create + default_create + +feature {NONE} -- Initialization + + default_create + do + set_endpoint ("") + set_authorize_url ("") + set_extractor ("") + set_callback_name ("") + set_protected_resource_url ("") + set_scope ("") + set_api_key ("") + set_api_secret ("") + set_name ("") + end + +feature -- Access + + endpoint: READABLE_STRING_32 + -- Url that receives the access token request. + + authorize_url: READABLE_STRING_32 + -- + + extractor: READABLE_STRING_32 + -- text, json + + + callback_name: READABLE_STRING_32 + -- consumer callback name + + protected_resource_url: READABLE_STRING_32 + -- consumer resource url + + scope: READABLE_STRING_32 + -- consumer scope + + api_key: READABLE_STRING_32 + -- consumer public key + + api_secret: READABLE_STRING_32 + -- consumer secret. + + name: READABLE_STRING_32 + -- consumer name. + + id: INTEGER_64 + -- unique identifier. + + + +feature -- Element change + + set_extractor (a_extractor: like extractor) + -- Assign `extractor' with `a_extractor'. + do + extractor := a_extractor + ensure + extractor_assigned: extractor = a_extractor + end + + set_authorize_url (a_authorize_url: like authorize_url) + -- Assign `authorize_url' with `a_authorize_url'. + do + authorize_url := a_authorize_url + ensure + authorize_url_assigned: authorize_url = a_authorize_url + end + + set_endpoint (a_endpoint: like endpoint) + -- Assign `endpoint' with `a_endpoint'. + do + endpoint := a_endpoint + ensure + endpoint_assigned: endpoint = a_endpoint + end + + set_callback_name (a_callback_name: like callback_name) + -- Assign `callback_name' with `a_callback_name'. + do + callback_name := a_callback_name + ensure + callback_name_assigned: callback_name = a_callback_name + end + + set_protected_resource_url (a_protected_resource_url: like protected_resource_url) + -- Assign `protected_resource_url' with `a_protected_resource_url'. + do + protected_resource_url := a_protected_resource_url + ensure + protected_resource_url_assigned: protected_resource_url = a_protected_resource_url + end + + set_scope (a_scope: like scope) + -- Assign `scope' with `a_scope'. + do + scope := a_scope + ensure + scope_assigned: scope = a_scope + end + + set_api_key (an_api_key: like api_key) + -- Assign `api_key' with `an_api_key'. + do + api_key := an_api_key + ensure + api_key_assigned: api_key = an_api_key + end + + set_api_secret (an_api_secret: like api_secret) + -- Assign `api_secret' with `an_api_secret'. + do + api_secret := an_api_secret + ensure + api_secret_assigned: api_secret = an_api_secret + end + + set_name (a_name: like name) + -- Assign `name' with `a_name'. + do + name := a_name + ensure + name_assigned: name = a_name + end + + set_id (an_id: like id) + -- Assign `id' with `an_id'. + do + id := an_id + ensure + id_assigned: id = an_id + end + +end diff --git a/modules/login/cms_user_oauth_api.e b/modules/login/cms_user_oauth_api.e index a9a7bb6..4e2ed38 100644 --- a/modules/login/cms_user_oauth_api.e +++ b/modules/login/cms_user_oauth_api.e @@ -31,34 +31,70 @@ feature {CMS_MODULE} -- Access user oauth storage. feature -- Access: OAuth2 Gmail - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER +-- user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER +-- do +-- Result := user_oauth_storage.user_oauth2_gmail_by_id (a_uid) +-- end + +-- user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER +-- do +-- Result := user_oauth_storage.user_by_oauth2_gmail_token (a_token) +-- end + + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER do - Result := user_oauth_storage.user_oauth2_gmail_by_id (a_uid) + Result := user_oauth_storage.user_oauth2_by_id (a_uid, a_consumer_table) end - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER do - Result := user_oauth_storage.user_by_oauth2_gmail_token (a_token) + Result := user_oauth_storage.user_by_oauth2_token (a_token, a_consumer_table) end + user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + do + Result := user_oauth_storage.user_by_oauth2_global_token (a_token) + end + + oauth2_consumers: LIST [STRING] + do + Result := user_oauth_storage.oauth2_consumers + end feature -- Change: OAuth2 Gmail - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) +-- new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) +-- -- Add a new user with oauth2 gmail authentication. +-- require +-- has_id: a_user.has_id +-- do +-- user_oauth_storage.new_user_oauth2_gmail (a_token, a_user_profile, a_user) +-- end + + +-- update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) +-- -- Updaate user `a_user' with oauth2 gmail authentication. +-- require +-- has_id: a_user.has_id +-- do +-- user_oauth_storage.update_user_oauth2_gmail (a_token, a_user_profile, a_user) +-- end + + new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) -- Add a new user with oauth2 gmail authentication. require has_id: a_user.has_id do - user_oauth_storage.new_user_oauth2_gmail (a_token, a_user_profile, a_user) + user_oauth_storage.new_user_oauth2 (a_token, a_user_profile, a_user, a_consumer_table) end - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) -- Updaate user `a_user' with oauth2 gmail authentication. require has_id: a_user.has_id do - user_oauth_storage.update_user_oauth2_gmail (a_token, a_user_profile, a_user) + user_oauth_storage.update_user_oauth2 (a_token, a_user_profile, a_user, a_consumer_table) end end diff --git a/modules/login/filter/oauth_gmail_filter.e b/modules/login/filter/oauth_filter.e similarity index 66% rename from modules/login/filter/oauth_gmail_filter.e rename to modules/login/filter/oauth_filter.e index c4f6310..d7c1f45 100644 --- a/modules/login/filter/oauth_gmail_filter.e +++ b/modules/login/filter/oauth_filter.e @@ -1,10 +1,10 @@ note - description: "Summary description for {OAUTH_GMAIL_FILTER}." + description: "Summary description for {OAUTH_FILTER}." date: "$Date$" revision: "$Revision$" class - OAUTH_GMAIL_FILTER + OAUTH_FILTER inherit WSF_URI_TEMPLATE_HANDLER @@ -36,14 +36,14 @@ feature -- Basic operations utf: UTF_CONVERTER do api.logger.put_debug (generator + ".execute ", Void) - if attached req.raw_header_data as l_raw_data then - api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void) - end +-- if attached req.raw_header_data as l_raw_data then +-- api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void) +-- end -- A valid user if - attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_gmail_session) as l_roc_auth_session_token + attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_session) as l_roc_auth_session_token then - if attached {CMS_USER} user_oauth_api.user_by_oauth2_gmail_token (l_roc_auth_session_token.value) as l_user then + if attached {CMS_USER} user_oauth_api.user_by_oauth2_global_token (l_roc_auth_session_token.value) as l_user then set_current_user (req, l_user) execute_next (req, res) else diff --git a/modules/login/login_constants.e b/modules/login/login_constants.e index 39886dc..ffae849 100644 --- a/modules/login/login_constants.e +++ b/modules/login/login_constants.e @@ -8,6 +8,6 @@ class feature -- Access - oauth_gmail_session: STRING = "EWF_ROC_OAUTH_GMAIL_SESSION_" - + oauth_session: STRING = "EWF_ROC_OAUTH_TOKEN_" + end diff --git a/modules/login/login_module.e b/modules/login/login_module.e index 4db73b7..e649c95 100644 --- a/modules/login/login_module.e +++ b/modules/login/login_module.e @@ -94,18 +94,47 @@ feature {CMS_API} -- Module management local sql: STRING l_setup: CMS_SETUP + l_params: detachable STRING_TABLE [detachable ANY] + l_consumers: LIST [STRING] do l_setup := api.setup -- Schema if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then - if not l_sql_storage.sql_table_exists ("oauth2_gmail") then + if not l_sql_storage.sql_table_exists ("oauth2_consumers") then --| Schema - l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_gmail.sql")) + l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_consumers.sql")) if l_sql_storage.has_error then api.logger.put_error ("Could not initialize database for blog module", generating_type) end + -- TODO workaround. + l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_consumers_initialize.sql")) + end + + -- TODO workaround, until we have an admin module + l_sql_storage.sql_query ("SELECT name FROM oauth2_consumers;", Void) + if l_sql_storage.has_error then + api.logger.put_error ("Could not initialize database for differnent consumerns", generating_type) + else + from + l_sql_storage.sql_start + create {ARRAYED_LIST[STRING]} l_consumers.make (2) + until + l_sql_storage.sql_after + loop + if attached l_sql_storage.sql_read_string (1) as l_name then + l_consumers.force ("oauth2_"+l_name) + end + l_sql_storage.sql_forth + end + across l_consumers as ic loop + if not l_sql_storage.sql_table_exists (ic.item) then + create l_params.make (1) + l_params.force (ic.item, "table_name") + l_sql_storage.sql_execute_file_script_with_params (l_setup.environment.path.extended ("scripts").extended ("oauth2_template.sql"), l_params) + end + end end api.storage.set_custom_value ("is_initialized", "module-" + name, "yes") end @@ -116,7 +145,6 @@ feature {CMS_API} -- Access: API user_oauth_api: detachable CMS_USER_OAUTH_API -- - feature -- Filters filters (a_api: CMS_API): detachable LIST [WSF_FILTER] @@ -124,7 +152,7 @@ feature -- Filters do create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) if attached user_oauth_api as l_user_oauth_api then - Result.extend (create {OAUTH_GMAIL_FILTER}.make (a_api, l_user_oauth_api)) + Result.extend (create {OAUTH_FILTER}.make (a_api, l_user_oauth_api)) end end @@ -164,8 +192,8 @@ feature -- Router a_router.handle_with_request_methods ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/account/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/account/login-with-google", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_google (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/account/oauthgmail", create {WSF_URI_AGENT_HANDLER}.make (agent handle_callback_gmail (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/account/login-with-oauth/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_oauth (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/account/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_callback_oauth (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post) end @@ -273,17 +301,15 @@ feature -- Hooks local r: CMS_RESPONSE l_url: STRING - l_oauth_gmail: OAUTH_LOGIN_GMAIL + l_oauth_gmail: OAUTH_LOGIN l_cookie: WSF_COOKIE do if - attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_gmail_session) as l_cookie_token and then + attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_session) as l_cookie_token and then attached {CMS_USER} current_user (req) as l_user then -- Logout gmail - create l_oauth_gmail.make (api, req.absolute_script_url ("")) - l_oauth_gmail.sign_out (l_cookie_token.value) - create l_cookie.make ({LOGIN_CONSTANTS}.oauth_gmail_session, l_cookie_token.value) + create l_cookie.make ({LOGIN_CONSTANTS}.oauth_session, l_cookie_token.value) l_cookie.set_path ("/") l_cookie.set_max_age (-1) res.add_cookie (l_cookie) @@ -563,7 +589,14 @@ feature {NONE} -- Block views loop l_tpl_block.set_value (ic.item, ic.key) end - a_response.add_block (l_tpl_block, "content") + if + attached user_oauth_api as l_auth_api and then + attached l_auth_api.oauth2_consumers as l_list + then + l_tpl_block.set_value (l_list, "oauth_consumers") + end + + a_response.add_block (l_tpl_block, "content") else debug ("cms") a_response.add_warning_message ("Error with block [" + a_block_id + "]") @@ -720,16 +753,25 @@ feature {NONE} -- Block views feature -- OAuth2 Login with google. - handle_login_with_google (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_login_with_oauth (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - l_oauth_gmail: OAUTH_LOGIN_GMAIL + l_oauth: OAUTH_LOGIN do - create l_oauth_gmail.make (api, req.absolute_script_url ("")) - if attached l_oauth_gmail.authorization_url as l_authorization then - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) - r.set_redirection (l_authorization) - r.execute + if + attached {WSF_STRING} req.path_parameter ("callback") as p_consumer and then + attached {CMS_OAUTH_CONSUMER} oauth_consumer_by_name (api, p_consumer.value) as l_consumer + then + create l_oauth.make (req.server_url, l_consumer) + if attached l_oauth.authorization_url as l_authorization_url then + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_redirection (l_authorization_url) + r.execute + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.set_main_content ("Bad request") + r.execute + end else create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) r.set_main_content ("Bad request") @@ -737,22 +779,25 @@ feature -- OAuth2 Login with google. end end - handle_callback_gmail (api: CMS_API; a_user_oauth_api: CMS_USER_OAUTH_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_callback_oauth (api: CMS_API; a_user_oauth_api: CMS_USER_OAUTH_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - l_auth_gmail: OAUTH_LOGIN_GMAIL + l_auth: OAUTH_LOGIN l_user_api: CMS_USER_API l_user: CMS_USER l_roles: LIST [CMS_USER_ROLE] l_cookie: WSF_COOKIE es: LOGIN_EMAIL_SERVICE do - if attached {WSF_STRING} req.query_parameter ("code") as l_code then - create l_auth_gmail.make (api, req.server_url) - l_auth_gmail.sign_request (l_code.value) + if attached {WSF_STRING} req.path_parameter ("callback") as l_callback and then + attached {CMS_OAUTH_CONSUMER} oauth_consumer_by_callback (api, l_callback.value) as l_consumer and then + attached {WSF_STRING} req.query_parameter ("code") as l_code + then + create l_auth.make (req.server_url, l_consumer) + l_auth.sign_request (l_code.value) if - attached l_auth_gmail.access_token as l_access_token and then - attached l_auth_gmail.user_profile as l_user_profile + attached l_auth.access_token as l_access_token and then + attached l_auth.user_profile as l_user_profile then create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) -- extract user email @@ -760,18 +805,18 @@ feature -- OAuth2 Login with google. l_user_api := api.user_api -- 1 if the user exit put it in the context if - attached l_auth_gmail.user_email as l_email + attached l_auth.user_email as l_email then if attached {CMS_USER} l_user_api.user_by_email (l_email) as p_user then -- User with email exist - if attached {CMS_USER} a_user_oauth_api.user_oauth2_gmail_by_id (p_user.id) then + if attached {CMS_USER} a_user_oauth_api.user_oauth2_by_id (p_user.id, "oauth2_" + l_consumer.name) then -- Update oauth entry - a_user_oauth_api.update_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) + a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, "oauth2_" + l_consumer.name ) else -- create a oauth entry - a_user_oauth_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) + a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, p_user, "oauth2_" + l_consumer.name ) end - create l_cookie.make ({LOGIN_CONSTANTS}.oauth_gmail_session, l_access_token.token) + create l_cookie.make ({LOGIN_CONSTANTS}.oauth_session, l_access_token.token) l_cookie.set_max_age (l_access_token.expires_in) l_cookie.set_path ("/") res.add_cookie (l_cookie) @@ -789,8 +834,8 @@ feature -- OAuth2 Login with google. l_user_api.new_user (l_user) -- Add oauth entry - a_user_oauth_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, l_user ) - create l_cookie.make ({LOGIN_CONSTANTS}.oauth_gmail_session, l_access_token.token) + a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, l_user, "oauth_" + l_consumer.name ) + create l_cookie.make ({LOGIN_CONSTANTS}.oauth_session, l_access_token.token) l_cookie.set_max_age (l_access_token.expires_in) l_cookie.set_path ("/") res.add_cookie (l_cookie) @@ -865,6 +910,120 @@ feature {NONE} -- Implementation: date and time Result := d.date_time end +feature --{NONE} -- Helper OAUTH Consumers. + + + oauth_consumer_by_name (a_api: CMS_API; a_name: READABLE_STRING_8): detachable CMS_OAUTH_CONSUMER + local + l_params: detachable STRING_TABLE [detachable ANY] + l_setup: CMS_SETUP + do + -- TODO workaround!!, move to the persistence layer + l_setup := a_api.setup + + -- Schema + if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then + + -- Todo workaround, move this to his own database layer. + create l_params.make (1) + l_params.force (a_name, "name") + l_sql_storage.sql_query ("SELECT * FROM oauth2_consumers where name =:name;", l_params) + if l_sql_storage.has_error then + a_api.logger.put_error ("Could not retrieve a consumer from the database", generating_type) + else + -- Fetch a Consumer + create Result + if attached l_sql_storage.sql_read_integer_64 (1) as l_id then + Result.set_id (l_id) + end + if attached l_sql_storage.sql_read_string_32 (2) as l_name then + Result.set_name (l_name) + end + if attached l_sql_storage.sql_read_string_32 (3) as l_api_secret then + Result.set_api_secret (l_api_secret) + end + if attached l_sql_storage.sql_read_string_32 (4) as l_api_key then + Result.set_api_key (l_api_key) + end + if attached l_sql_storage.sql_read_string_32 (5) as l_scope then + Result.set_scope (l_scope) + end + if attached l_sql_storage.sql_read_string_32 (6) as l_resource_url then + Result.set_protected_resource_url (l_resource_url) + end + if attached l_sql_storage.sql_read_string_32 (7) as l_callback_name then + Result.set_callback_name (l_callback_name) + end + if attached l_sql_storage.sql_read_string_32 (8) as l_extractor then + Result.set_extractor (l_extractor) + end + if attached l_sql_storage.sql_read_string_32 (9) as l_authorize_url then + Result.set_authorize_url (l_authorize_url) + end + if attached l_sql_storage.sql_read_string_32 (10) as l_endpoint then + Result.set_endpoint (l_endpoint) + end + end + end + end + + + oauth_consumer_by_callback (a_api: CMS_API; a_name: READABLE_STRING_8): detachable CMS_OAUTH_CONSUMER + local + l_params: detachable STRING_TABLE [detachable ANY] + l_setup: CMS_SETUP + do + -- TODO workaround !!! move to the persistence layer. + l_setup := a_api.setup + + + -- Schema + if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then + + -- Todo workaround, move this to his own database layer. + create l_params.make (1) + l_params.force (a_name, "name") + l_sql_storage.sql_query ("SELECT * FROM oauth2_consumers where callback_name =:name;", l_params) + if l_sql_storage.has_error then + a_api.logger.put_error ("Could not retrieve a consumer from the database", generating_type) + else + -- Fetch a Consumer + create Result + if attached l_sql_storage.sql_read_integer_64 (1) as l_id then + Result.set_id (l_id) + end + if attached l_sql_storage.sql_read_string_32 (2) as l_name then + Result.set_name (l_name) + end + if attached l_sql_storage.sql_read_string_32 (3) as l_api_secret then + Result.set_api_secret (l_api_secret) + end + if attached l_sql_storage.sql_read_string_32 (4) as l_api_key then + Result.set_api_key (l_api_key) + end + if attached l_sql_storage.sql_read_string_32 (5) as l_scope then + Result.set_scope (l_scope) + end + if attached l_sql_storage.sql_read_string_32 (6) as l_resource_url then + Result.set_protected_resource_url (l_resource_url) + end + if attached l_sql_storage.sql_read_string_32 (7) as l_callback_name then + Result.set_callback_name (l_callback_name) + end + if attached l_sql_storage.sql_read_string_32 (8) as l_extractor then + Result.set_extractor (l_extractor) + end + if attached l_sql_storage.sql_read_string_32 (9) as l_authorize_url then + Result.set_authorize_url (l_authorize_url) + end + if attached l_sql_storage.sql_read_string_32 (10) as l_endpoint then + Result.set_endpoint (l_endpoint) + end + end + end + end + + note copyright: "Copyright (c) 1984-2013, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/modules/login/oauth_login_gmail.e b/modules/login/oauth_login.e similarity index 61% rename from modules/login/oauth_login_gmail.e rename to modules/login/oauth_login.e index f84c622..6c100da 100644 --- a/modules/login/oauth_login_gmail.e +++ b/modules/login/oauth_login.e @@ -1,10 +1,10 @@ note - description: "OAuth workflow for Gmails." + description: "OAuth workflow" date: "$Date$" revision: "$Revision$" class - OAUTH_LOGIN_GMAIL + OAUTH_LOGIN inherit @@ -15,51 +15,25 @@ create feature {NONE} -- Initialization - make (a_cms_api:CMS_API a_host: READABLE_STRING_32) + make (a_host: READABLE_STRING_32; a_consumer: CMS_OAUTH_CONSUMER) -- Create an object with the host `a_host'. do - cms_api := a_cms_api - initilize + initilize (a_consumer) create config.make_default (api_key, api_secret) - config.set_callback (a_host + "/account/oauthgmail") + config.set_callback (a_host + "/account/"+ a_consumer.callback_name) config.set_scope (scope) - create goauth - api_service := goauth.create_service (config) - ensure - cms_api_set: cms_api = a_cms_api + --Todo create a generic OAUTH_20_GENERIC_API + create oauth_api.make (a_consumer.endpoint, a_consumer.authorize_url, a_consumer.extractor) + api_service := oauth_api.create_service (config) end - initilize - local - utf: UTF_CONVERTER + initilize (a_consumer: CMS_OAUTH_CONSUMER) do --Use configuration values if any if not defaul - api_key := "KEY" - api_secret := "SECRET" - scope := "email" - - api_revoke := "[https://accounts.google.com/o/oauth2/revoke?token=$ACCESS_TOKEN]" - protected_resource_url := "https://www.googleapis.com/plus/v1/people/me" - - - if attached {CONFIG_READER} cms_api.module_configuration ("login", "oauth2_gmail") as cfg then - if attached cfg.text_item ("api_secret") as l_api_secret then - api_secret := utf.utf_32_string_to_utf_8_string_8 (l_api_secret) - end - if attached cfg.text_item ("api_key") as l_api_key then - api_key := utf.utf_32_string_to_utf_8_string_8 (l_api_key) - end - if attached cfg.text_item ("scope") as l_scope then - scope := utf.utf_32_string_to_utf_8_string_8 (l_scope) - end - if attached cfg.text_item ("api_revoke") as l_api_revoke then - api_revoke := utf.utf_32_string_to_utf_8_string_8 (l_api_revoke) - end - if attached cfg.text_item ("protected_resource_url") as l_resource_url then - protected_resource_url := utf.utf_32_string_to_utf_8_string_8 (l_resource_url) - end - end - + api_key := a_consumer.api_key + api_secret := a_consumer.api_secret + scope := a_consumer.scope + protected_resource_url := a_consumer.protected_resource_url end feature -- Access @@ -103,26 +77,11 @@ feature -- Access end end - sign_out (a_code: READABLE_STRING_32) - -- Invalidate the current OAuth access token `a_code'. - local - l_revoke: STRING - request: OAUTH_REQUEST - do - create l_revoke.make_from_string (api_revoke) - l_revoke.replace_substring_all ("$ACCESS_TOKEN", a_code) - create request.make ("POST", l_revoke) - if attached {OAUTH_RESPONSE} request.execute as l_response then - -- do nothing - write_debug_log (generator + ".sign_out response [" + l_response.status.out + "]") - check invalidate_ok: l_response.status = {HTTP_CONSTANTS}.ok end - end - end - user_email: detachable READABLE_STRING_32 -- Retrieve user email if any. local l_json: JSON_CONFIG + utf: UTF_CONVERTER do if attached user_profile as l_profile then create l_json.make_from_string (l_profile) @@ -132,6 +91,8 @@ feature -- Access attached {JSON_STRING} l_object.item ("value") as l_email then Result := l_email.item + elseif attached {JSON_STRING} l_json.item ("email") as l_email then + Result := l_email.unescaped_string_32 end end end @@ -146,7 +107,7 @@ feature -- Access feature {NONE} -- Implementation - goauth: OAUTH_20_GOOGLE_API + oauth_api: CMS_OAUTH_20_GENERIC_API -- OAuth 2.0 Google API. config: OAUTH_CONFIG @@ -164,16 +125,10 @@ feature {NONE} -- Implementation scope: STRING -- api scope to access protected resources. - api_revoke: STRING - -- Revoke url - protected_resource_url: STRING -- Resource url. empty_token: detachable OAUTH_TOKEN -- fake token. - cms_api: CMS_API - -- CMS API. - end diff --git a/modules/login/persistence/cms_oauth_20_generic_api.e b/modules/login/persistence/cms_oauth_20_generic_api.e new file mode 100644 index 0000000..149c340 --- /dev/null +++ b/modules/login/persistence/cms_oauth_20_generic_api.e @@ -0,0 +1,94 @@ +note + description: "Generic OAUTH2 API" + date: "$Date$" + revision: "$Revision$" + +class + CMS_OAUTH_20_GENERIC_API + +inherit + + OAUTH_20_API + redefine + access_token_extractor, + access_token_verb + end + +create + make + +feature {NONE} -- Initialize + + make (a_endpoint: READABLE_STRING_32; a_authorize_url: READABLE_STRING_32; a_extractor: READABLE_STRING_32) + do + endpoint := a_endpoint + authorize_url := a_authorize_url + extractor := a_extractor + ensure + endpoint_set: endpoint = a_endpoint + authorize_url_set: authorize_url = a_authorize_url + extractor_set: extractor = a_authorize_url + end + + endpoint: READABLE_STRING_32 + -- Url that receives the access token request. + + authorize_url: READABLE_STRING_32 + -- + + extractor: READABLE_STRING_32 + -- text, json + +feature -- Access + + access_token_extractor: ACCESS_TOKEN_EXTRACTOR + -- Return token extractor, by default TOKEN_EXTRACTOR_20. + do + if extractor.is_case_insensitive_equal_general ("json") then + create {JSON_TOKEN_EXTRACTOR} Result + else + create {TOKEN_EXTRACTOR_20} Result + end + end + + access_token_verb: READABLE_STRING_GENERAL + do + Result := "POST" + end + + access_token_endpoint: READABLE_STRING_GENERAL + -- Url that receives the access token request + do + create {STRING_32} Result.make_from_string (endpoint) + end + + authorization_url (config: OAUTH_CONFIG): detachable READABLE_STRING_GENERAL + -- Url where you should redirect your users to authneticate + local + l_result: STRING_32 + do + if attached config.scope as l_scope then + create {STRING_32} l_result.make_from_string (authorize_url + SCOPED_AUTHORIZE_URL) + l_result.replace_substring_all ("$CLIENT_ID", config.api_key.as_string_8) + if attached config.callback as l_callback then + l_result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_32)) + end + if attached config.callback as l_callback then + l_result.replace_substring_all ("$SCOPE", (create {OAUTH_ENCODER}).encoded_string (l_scope.as_string_32)) + Result := l_result + end + else + create {STRING_32} l_result.make_from_string (authorize_url + SCOPED_AUTHORIZE_URL) + l_result.replace_substring_all ("$CLIENT_ID", config.api_key.as_string_8) + if attached config.callback as l_callback then + l_result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_32)) + end + end + end + +feature -- Implementation + + Scoped_authorize_url: STRING = "&scope=$SCOPE"; + + +end diff --git a/modules/login/persistence/cms_user_oauth_storage_i.e b/modules/login/persistence/cms_user_oauth_storage_i.e index 8932faf..6011ee7 100644 --- a/modules/login/persistence/cms_user_oauth_storage_i.e +++ b/modules/login/persistence/cms_user_oauth_storage_i.e @@ -18,27 +18,35 @@ feature -- Error Handling feature -- Access - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER - -- CMS User with Oauth gmail credential by id if any. + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + -- CMS User with Oauth credential by id if any. deferred end - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER - -- -- CMS User with Oauth gmail credential by access token `a_token' if any. + user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + -- -- CMS User with Oauth credential by access token `a_token' if any. + deferred + end + + user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + -- + deferred + end + + oauth2_consumers: LIST [STRING] deferred end feature -- Change: User Oauth2 - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Add a new user with oauth2 gmail authentication. + new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) + -- Add a new user with oauth2 authentication. deferred end - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Update user `a_user' with oauth2 gmail authentication. + update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 ) + -- Update user `a_user' with oauth2 authentication. deferred end - end diff --git a/modules/login/persistence/cms_user_oauth_storage_null.e b/modules/login/persistence/cms_user_oauth_storage_null.e index e20e07f..a907093 100644 --- a/modules/login/persistence/cms_user_oauth_storage_null.e +++ b/modules/login/persistence/cms_user_oauth_storage_null.e @@ -22,25 +22,34 @@ feature -- Error handler feature -- Access - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER - -- CMS User with Oauth gmail credential by id if any. + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + -- CMS User with Oauth credential by id if any. do end - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER - -- -- CMS User with Oauth gmail credential by access token `a_token' if any. + user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + -- -- CMS User with Oauth credential by access token `a_token' if any. do end + user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + do + end + + oauth2_consumers: LIST [STRING] + do + create {ARRAYED_LIST[STRING]} Result.make (0) + end + feature -- Change: User Oauth2 - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Add a new user with oauth2 gmail authentication. + new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) + -- Add a new user with oauth2 authentication. do end - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Update user `a_user' with oauth2 gmail authentication. + update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 ) + -- Update user `a_user' with oauth2 authentication. do end diff --git a/modules/login/persistence/cms_user_oauth_storage_sql.e b/modules/login/persistence/cms_user_oauth_storage_sql.e index 9a9a692..c0585f6 100644 --- a/modules/login/persistence/cms_user_oauth_storage_sql.e +++ b/modules/login/persistence/cms_user_oauth_storage_sql.e @@ -22,16 +22,39 @@ create feature -- Access User Outh Gmail - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + + user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + local + l_list: LIST[STRING] + do + error_handler.reset + write_information_log (generator + ".user_by_oauth2_global_token") + l_list := oauth2_consumers + from + l_list.start + until + l_list.after or attached Result + loop + if attached {CMS_USER} user_by_oauth2_token (a_token, "oauth2_"+l_list.item) as l_user then + Result := l_user + end + l_list.forth + end + end + + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER -- local l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING do error_handler.reset - write_information_log (generator + ".user_oauth2_gmail_by_id") + write_information_log (generator + ".user_oauth2_by_id") create l_parameters.make (1) l_parameters.put (a_uid, "uid") - sql_query (select_user_oauth2_gmail_by_id, l_parameters) + create l_string.make_from_string (select_user_oauth2_template_by_id) + l_string.replace_substring_all ("$table_name", a_consumer_table) + sql_query (l_string, l_parameters) if sql_rows_count = 1 then Result := fetch_user else @@ -39,16 +62,19 @@ feature -- Access User Outh Gmail end end - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER -- local l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING do error_handler.reset - write_information_log (generator + ".user_by_oauth2_gmail_token") + write_information_log (generator + ".user_by_oauth2_token") create l_parameters.make (1) l_parameters.put (a_token, "token") - sql_query (select_user_by_oauth2_gmail_token, l_parameters) + create l_string.make_from_string (select_user_by_oauth2_template_token) + l_string.replace_substring_all ("$table_name", a_consumer_table) + sql_query (l_string, l_parameters) if sql_rows_count = 1 then Result := fetch_user else @@ -56,43 +82,71 @@ feature -- Access User Outh Gmail end end + oauth2_consumers: LIST[STRING] + -- Return a list of consumers, or empty + do + error_handler.reset + create {ARRAYED_LIST[STRING]}Result.make (0) + write_information_log (generator + ".user_by_oauth2_token") + sql_query (Sql_oauth_consumers, Void) + if not has_error then + from + sql_start + until + sql_after + loop + if attached sql_read_string (1) as l_name then + Result.force (l_name) + end + sql_forth + end + end + end + feature -- Change: User Oauth2 Gmail - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- . + new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) + -- Add a new user with oauth2 authentication. + -- . local l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING do error_handler.reset sql_begin_transaction - write_information_log (generator + ".new_user_oauth2_gmail") + write_information_log (generator + ".new_user_oauth2") create l_parameters.make (4) l_parameters.put (a_user.id, "uid") l_parameters.put (a_token, "token") l_parameters.put (a_user_profile, "profile") l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") - sql_change (sql_insert_oauth2_gmail, l_parameters) + create l_string.make_from_string (sql_insert_oauth2_template) + l_string.replace_substring_all ("$table_name", a_consumer_table) + sql_change (l_string, l_parameters) sql_commit_transaction end - - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 ) + -- Update user `a_user' with oauth2 authentication. -- local l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING do error_handler.reset sql_begin_transaction - write_information_log (generator + ".new_user_oauth2_gmail") + write_information_log (generator + ".new_user_oauth2") create l_parameters.make (4) l_parameters.put (a_user.id, "uid") l_parameters.put (a_token, "token") l_parameters.put (a_user_profile, "profile") - sql_change (sql_update_oauth2_gmail, l_parameters) + create l_string.make_from_string (sql_update_oauth2_template) + l_string.replace_substring_all ("$table_name", a_consumer_table) + sql_change (l_string, l_parameters) sql_commit_transaction end @@ -135,15 +189,17 @@ feature {NONE} -- Implementation: User end end -feature {NONE}-- User Oauth2 Gmail. +feature -- {NONE} User OAuth2 - Sql_insert_oauth2_gmail: STRING = "INSERT INTO oauth2_gmail (uid, access_token, details, created) VALUES (:uid, :token, :profile, :utc_date);" + Select_user_by_oauth2_template_token: STRING = "SELECT u.* FROM users as u JOIN $table_name as og ON og.uid = u.uid and og.access_token = :token;" - Sql_update_oauth2_gmail: STRING = "UPDATE oauth2_gmail SET access_token = :token, details = :profile WHERE uid =:uid;" + Select_user_oauth2_template_by_id: STRING = "SELECT u.* FROM users as u JOIN $table_name as og ON og.uid = u.uid and og.uid = :uid;" - Select_user_by_oauth2_gmail_token: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.access_token = :token;" - Select_user_oauth2_gmail_by_id: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.uid = :uid;" + Sql_insert_oauth2_template: STRING = "INSERT INTO $table_name (uid, access_token, details, created) VALUES (:uid, :token, :profile, :utc_date);" + Sql_update_oauth2_template: STRING = "UPDATE $table_name SET access_token = :token, details = :profile WHERE uid =:uid;" + + Sql_oauth_consumers: STRING = "SELECT name FROM oauth2_consumers"; end diff --git a/src/persistence/sql/cms_storage_sql_i.e b/src/persistence/sql/cms_storage_sql_i.e index 8f38ab0..fa9f91b 100644 --- a/src/persistence/sql/cms_storage_sql_i.e +++ b/src/persistence/sql/cms_storage_sql_i.e @@ -76,7 +76,7 @@ feature -- Operation i := a_sql_statement.index_of (':', i) if i = 0 then i := n -- exit - else + elseif a_sql_statement.at (i-1).is_equal ('%'') or else a_sql_statement.at (i-1).is_equal ('%"') or else a_sql_statement.at (i-1).is_equal (' ') or else a_sql_statement.at (i-1).is_equal ('=') then from j := i + 1 until @@ -124,6 +124,30 @@ feature -- Operation feature -- Helper + sql_execute_file_script_with_params (a_path: PATH; a_params: detachable STRING_TABLE [detachable ANY]) + -- Execute SQL script from `a_path' and with params `a_params'. + local + f: PLAIN_TEXT_FILE + sql: STRING + do + create f.make_with_path (a_path) + if f.exists and then f.is_access_readable then + create sql.make (f.count) + f.open_read + from + f.start + until + f.exhausted or f.end_of_file + loop + f.read_stream_thread_aware (1_024) + sql.append (f.last_string) + end + f.close + sql_execute_script_with_params (sql, a_params) + end + end + + sql_execute_file_script (a_path: PATH) -- Execute SQL script from `a_path'. local @@ -157,6 +181,14 @@ feature -- Helper -- sql_commit_transaction end + sql_execute_script_with_params (a_sql_script: STRING; a_params: detachable STRING_TABLE [detachable ANY]) + -- Execute SQL script. + -- i.e: multiple SQL statements. + do + reset_error + sql_change (a_sql_script, a_params) + end + sql_table_exists (a_table_name: READABLE_STRING_8): BOOLEAN -- Does table `a_table_name' exists? local