diff --git a/modules/auth/cms_authentication_constants.e b/modules/auth/cms_authentication_constants.e index 6278ed8..d0debbb 100644 --- a/modules/auth/cms_authentication_constants.e +++ b/modules/auth/cms_authentication_constants.e @@ -9,5 +9,7 @@ class feature -- Access oauth_session: STRING = "EWF_ROC_OAUTH_TOKEN_" + -- Name of Cookie used to keep the session info. + -- FIXME: make this configurable. end diff --git a/modules/auth/cms_authentication_email_service_parameters.e b/modules/auth/cms_authentication_email_service_parameters.e index 20746f2..5db145c 100644 --- a/modules/auth/cms_authentication_email_service_parameters.e +++ b/modules/auth/cms_authentication_email_service_parameters.e @@ -104,57 +104,48 @@ feature -- Access contact_subject_password: IMMUTABLE_STRING_8 contact_subject_oauth: IMMUTABLE_STRING_8 - - account_activation: STRING -- Account activation template email message. - local - p: PATH do - p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_activation.html") - if attached read_template_file (p) as l_content then - Result := l_content - else - create Result.make_from_string (template_account_activation) - end + Result := template_string ("account_activation.html", default_template_account_activation) end account_re_activation: STRING -- Account re_activation template email message. - local - p: PATH do - p := setup.environment.config_path.extended ("modules").extended ("login").extended("accunt_re_activation.html") - if attached read_template_file (p) as l_content then - Result := l_content - else - create Result.make_from_string (template_account_re_activation) - end + Result := template_string ("accunt_re_activation.html", default_template_account_re_activation) end account_password: STRING -- Account password template email message. - local - p: PATH do - p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_new_password.html") - if attached read_template_file (p) as l_content then - Result := l_content - else - create Result.make_from_string (template_account_new_password) - end + Result := template_string ("account_new_password.html", default_template_account_new_password) end account_welcome: STRING -- Account welcome template email message. + do + Result := template_string ("account_welcome.html", default_template_account_welcome) + end + +feature {NONE} -- Implementation: Template + + template_path (a_name: READABLE_STRING_GENERAL): PATH + -- Location of template named `a_name'. + do + Result := setup.environment.config_path.extended ("modules").extended ("login").extended (a_name) + end + + template_string (a_name: READABLE_STRING_GENERAL; a_default: STRING): STRING + -- Content of template named `a_name', or `a_default' if template is not found. local p: PATH do - p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_welcome.html") + p := template_path ("account_activation.html") if attached read_template_file (p) as l_content then Result := l_content else - create Result.make_from_string (template_account_welcome) + create Result.make_from_string (a_default) end end @@ -162,16 +153,17 @@ feature {NONE} -- Implementation setup: CMS_SETUP - read_template_file (a_path: PATH): detachable STRING -- Read the content of the file at path `a_path'. local l_file: FILE + n: INTEGER do create {PLAIN_TEXT_FILE} l_file.make_with_path (a_path) if l_file.exists and then l_file.is_readable then + n := l_file.count l_file.open_read - l_file.read_stream (l_file.count) + l_file.read_stream (n) Result := l_file.last_string l_file.close else @@ -182,7 +174,7 @@ feature {NONE} -- Implementation feature {NONE} -- Message email - template_account_activation: STRING= "[ + default_template_account_activation: STRING = "[
@@ -195,7 +187,7 @@ feature {NONE} -- Message emailThank you for registering at ROC CMS
-To complete your registration, please click on this link to activate your account:
+
To complete your registration, please click on the following link to activate your account:
Thank you for joining us.
@@ -204,7 +196,7 @@ feature {NONE} -- Message email ]" - template_account_re_activation: STRING= "[ + default_template_account_re_activation: STRING = "[ @@ -215,9 +207,9 @@ feature {NONE} -- Message email -You have request a new activation token atROC CMS
+You have requested a new activation token at ROC CMS
-To complete your registration, please click on this link to activate your account:
+
To complete your registration, please click on the following link to activate your account:
Thank you for joining us.
@@ -227,7 +219,7 @@ feature {NONE} -- Message email - template_account_new_password: STRING= "[ + default_template_account_new_password: STRING = "[ @@ -240,7 +232,7 @@ feature {NONE} -- Message emailYou have required a new password at ROC CMS
-To complete your request, please click on this link to genereate a new password:
+
To complete your request, please click on this link to generate a new password:
@@ -248,7 +240,7 @@ feature {NONE} -- Message email ]" - template_account_welcome: STRING= "[ + default_template_account_welcome: STRING = "[ diff --git a/modules/auth/cms_authentication_module.e b/modules/auth/cms_authentication_module.e index 58e1cdd..21fc490 100644 --- a/modules/auth/cms_authentication_module.e +++ b/modules/auth/cms_authentication_module.e @@ -50,8 +50,8 @@ feature {NONE} -- Initialization do name := "login" version := "1.0" - description := "Eiffel login module" - package := "login" + description := "Authentication module" + package := "authentication" create root_dir.make_current cache_duration := 0 @@ -235,7 +235,7 @@ feature -- Hooks local l_string: STRING do - Result := <<"login","register","reactivate","new_password", "reset_password">> + Result := <<"login", "register", "reactivate", "new_password", "reset_password">> create l_string.make_empty across Result as ic loop l_string.append (ic.item) @@ -248,27 +248,27 @@ feature -- Hooks do if a_block_id.is_case_insensitive_equal_general ("login") and then - a_response.request.path_info.starts_with ("/account/roc-login") + a_response.location.starts_with ("account/roc-login") then get_block_view_login (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("register") and then - a_response.request.path_info.starts_with ("/account/roc-register") + a_response.location.starts_with ("account/roc-register") then get_block_view_register (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("reactivate") and then - a_response.request.path_info.starts_with ("/account/reactivate") + a_response.location.starts_with ("account/reactivate") then get_block_view_reactivate (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("new_password") and then - a_response.request.path_info.starts_with ("/account/new-password") + a_response.location.starts_with ("account/new-password") then get_block_view_new_password (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("reset_password") and then - a_response.request.path_info.starts_with ("/account/reset-password") + a_response.location.starts_with ("account/reset-password") then get_block_view_reset_password (a_block_id, a_response) end @@ -306,8 +306,7 @@ feature -- Hooks else create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) r.set_status_code ({HTTP_CONSTANTS}.found) - l_url := req.absolute_script_url ("") - l_url.append ("/basic_auth_logoff") + l_url := req.absolute_script_url ("/basic_auth_logoff") r.set_redirection (l_url) r.execute end @@ -321,7 +320,7 @@ feature -- Hooks l_roles: LIST [CMS_USER_ROLE] l_exist: BOOLEAN es: CMS_AUTHENTICATON_EMAIL_SERVICE - l_link: STRING + l_url: STRING l_token: STRING do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) @@ -359,15 +358,12 @@ feature -- Hooks -- Create activation token l_token := new_token l_user_api.new_activation (l_token, u.id) - create l_link.make_from_string (req.server_url) - l_link.append ("/account/activate/") - l_link.append (l_token) - + l_url := req.absolute_script_url ("/account/activate/" + l_token) -- Send Email create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) write_debug_log (generator + ".handle register: send_contact_email") - es.send_contact_email (l_email.value, l_link) + es.send_contact_email (l_email.value, l_url) else r.values.force (l_name.value, "name") @@ -401,8 +397,7 @@ feature -- Hooks -- the token does not exist, or it was already used. r.set_status_code ({HTTP_CONSTANTS}.bad_request) r.set_value ("Account not activated", "optional_content_type") - r.set_main_content ("
The token "+ l_token.value +" is not valid Reactivate Account
" ) - + r.set_main_content ("The token " + l_token.value +" is not valid " + r.link ("Reactivate Account", "account/reactivate", Void) + "
") end r.execute else @@ -418,7 +413,7 @@ feature -- Hooks es: CMS_AUTHENTICATON_EMAIL_SERVICE l_user_api: CMS_USER_API l_token: STRING - l_link: STRING + l_url: STRING do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) if req.is_post_request_method then @@ -434,14 +429,12 @@ feature -- Hooks else l_token := new_token l_user_api.new_activation (l_token, l_user.id) - create l_link.make_from_string (req.server_url) - l_link.append ("/account/activate/") - l_link.append (l_token) + l_url := req.absolute_script_url ("/account/activate/" + l_token) -- Send Email create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) write_debug_log (generator + ".handle register: send_contact_activation_email") - es.send_contact_activation_email (l_email.value, l_link) + es.send_contact_activation_email (l_email.value, l_url) end else r.values.force ("The email does not exist or !", "error_email") @@ -460,7 +453,7 @@ feature -- Hooks es: CMS_AUTHENTICATON_EMAIL_SERVICE l_user_api: CMS_USER_API l_token: STRING - l_link: STRING + l_url: STRING do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) if req.is_post_request_method then @@ -470,14 +463,12 @@ feature -- Hooks -- User exist create a new token and send a new email. l_token := new_token l_user_api.new_password (l_token, l_user.id) - create l_link.make_from_string (req.server_url) - l_link.append ("/account/reset-password?token=") - l_link.append (l_token) + l_url := req.absolute_script_url ("/account/reset-password?token=" + l_token) -- Send Email create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) write_debug_log (generator + ".handle register: send_contact_password_email") - es.send_contact_password_email (l_email.value, l_link) + es.send_contact_password_email (l_email.value, l_url) else r.values.force ("The email does not exist !", "error_email") r.values.force (l_email.value, "email") @@ -499,7 +490,7 @@ feature -- Hooks if attached {WSF_STRING} req.query_parameter ("token") as l_token then r.values.force (l_token.value, "token") if l_user_api.user_by_password_token (l_token.value) = Void then - r.values.force ("The token " + l_token.value + " is not valid, click here to generate a new token.", "error_token") + r.values.force ("The token " + l_token.value + " is not valid, " + r.link ("click here" , "account/new-password", Void) + " to generate a new token.", "error_token") r.set_status_code ({HTTP_CONSTANTS}.bad_request) end end @@ -773,9 +764,9 @@ feature -- OAuth2 Login with google. if 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 + if attached 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_by_id (p_user.id, l_consumer.name) then + if attached a_user_oauth_api.user_oauth2_by_id (p_user.id, l_consumer.name) then -- Update oauth entry a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name ) else @@ -788,7 +779,7 @@ feature -- OAuth2 Login with google. res.add_cookie (l_cookie) else - create {ARRAYED_LIST [CMS_USER_ROLE]}l_roles.make (1) + create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (1) l_roles.force (l_user_api.authenticated_user_role) -- Create a new user and oauth entry @@ -813,9 +804,8 @@ feature -- OAuth2 Login with google. write_debug_log (generator + ".handle register: send_contact_welcome_email") es.send_contact_welcome_email (l_email, "") end - else end - r.set_redirection (req.absolute_script_url ("")) + r.set_redirection (r.front_page_url) r.execute end @@ -844,8 +834,6 @@ feature {NONE} -- Token Generation Result := l_token end - - feature {NONE} -- Implementation: date and time http_date_format_to_date (s: READABLE_STRING_8): detachable DATE_TIME diff --git a/modules/auth/cms_oauth_20_api.e b/modules/auth/cms_oauth_20_api.e index 27739b2..a1876d6 100644 --- a/modules/auth/cms_oauth_20_api.e +++ b/modules/auth/cms_oauth_20_api.e @@ -34,20 +34,20 @@ feature {CMS_MODULE} -- Access: User oauth storage. feature -- Access: User Oauth20 - user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_32): detachable CMS_USER - -- Retrieve a user by id `a_uid' for the consumer `a_consumer', if aby. + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER + -- Retrieve a user by id `a_uid' for the consumer `a_consumer', if any. do Result := oauth_20_storage.user_oauth2_by_id (a_uid, a_consumer) end - user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer: READABLE_STRING_32): detachable CMS_USER + user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER -- Retrieve a user by token `a_token' for the consumer `a_consumer'. do Result := oauth_20_storage.user_oauth2_by_token (a_token, a_consumer) end - user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER - -- Retrieve a user by token `a_token' searching in all the registered consumers in the system. + user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL): detachable CMS_USER + -- Retrieve user by token `a_token' searching in all the registered consumers in the system. do Result := oauth_20_storage.user_oauth2_without_consumer_by_token (a_token) end @@ -75,7 +75,7 @@ feature -- Access: Consumers OAuth20 feature -- Change: User OAuth20 - new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32) + new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_GENERAL) -- Add a new user with oauth20 using the consumer `a_consumer'. require has_id: a_user.has_id @@ -84,7 +84,7 @@ feature -- Change: User OAuth20 end - 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_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL) -- Updaate user `a_user' with oauth2 for the consumer `a_consumer'. require has_id: a_user.has_id diff --git a/modules/auth/cms_oauth_20_consumer.e b/modules/auth/cms_oauth_20_consumer.e index 79f3421..1e14806 100644 --- a/modules/auth/cms_oauth_20_consumer.e +++ b/modules/auth/cms_oauth_20_consumer.e @@ -8,17 +8,23 @@ class CMS_OAUTH_20_CONSUMER inherit - ANY redefine default_create end create - default_create + default_create, + make_with_id feature {NONE} -- Initialization + make_with_id (a_id: like id) + do + id := a_id + default_create + end + default_create do set_endpoint ("") @@ -34,29 +40,29 @@ feature {NONE} -- Initialization feature -- Access - endpoint: READABLE_STRING_32 + endpoint: READABLE_STRING_8 -- Url that receives the access token request. - authorize_url: READABLE_STRING_32 + authorize_url: READABLE_STRING_8 -- - extractor: READABLE_STRING_32 + extractor: READABLE_STRING_8 -- text, json - callback_name: READABLE_STRING_32 + callback_name: READABLE_STRING_8 -- consumer callback name - protected_resource_url: READABLE_STRING_32 + protected_resource_url: READABLE_STRING_8 -- consumer resource url - scope: READABLE_STRING_32 + scope: READABLE_STRING_8 -- consumer scope - api_key: READABLE_STRING_32 + api_key: READABLE_STRING_8 -- consumer public key - api_secret: READABLE_STRING_32 + api_secret: READABLE_STRING_8 -- consumer secret. name: READABLE_STRING_32 @@ -65,8 +71,6 @@ feature -- Access id: INTEGER_64 -- unique identifier. - - feature -- Element change set_extractor (a_extractor: like extractor) diff --git a/modules/auth/filter/cms_oauth_20_filter.e b/modules/auth/filter/cms_oauth_20_filter.e index ef250ee..30e2646 100644 --- a/modules/auth/filter/cms_oauth_20_filter.e +++ b/modules/auth/filter/cms_oauth_20_filter.e @@ -32,27 +32,21 @@ feature -- Basic operations execute (req: WSF_REQUEST; res: WSF_RESPONSE) -- Execute the filter. - local 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 -- A valid user if attached {WSF_STRING} req.cookie ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session) as l_roc_auth_session_token then - if attached {CMS_USER} user_oauth_api.user_oauth2_without_consumer_by_token (l_roc_auth_session_token.value) as l_user then + if attached user_oauth_api.user_oauth2_without_consumer_by_token (l_roc_auth_session_token.value) as l_user then set_current_user (req, l_user) - execute_next (req, res) else api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void) - execute_next (req, res) end else api.logger.put_debug (generator + ".execute without authentication", Void) - execute_next (req, res) end + execute_next (req, res) end end diff --git a/modules/auth/persistence/cms_oauth_20_storage_i.e b/modules/auth/persistence/cms_oauth_20_storage_i.e index 2357707..d3a65cb 100644 --- a/modules/auth/persistence/cms_oauth_20_storage_i.e +++ b/modules/auth/persistence/cms_oauth_20_storage_i.e @@ -20,18 +20,18 @@ feature -- Error Handling feature -- Access: Users - user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER -- Retrieve a user by id `a_uid' for the consumer `a_consumer', if aby. deferred end - user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER -- Retrieve a user by token `a_token' for the consumer `a_consumer'. deferred end - user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER - -- Retrieve a user by token `a_token' searching in all the registered consumers in the system. + user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL): detachable CMS_USER + -- Retrieve user by token `a_token' searching in all the registered consumers in the system. deferred end @@ -53,12 +53,12 @@ feature -- Access: Consumers feature -- Change: User Oauth2 - new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) + new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL) -- Add a new user with oauth2 authentication. deferred end - 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_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL ) -- Update user `a_user' with oauth2 authentication. deferred end diff --git a/modules/auth/persistence/cms_oauth_20_storage_null.e b/modules/auth/persistence/cms_oauth_20_storage_null.e index d15d092..675b9be 100644 --- a/modules/auth/persistence/cms_oauth_20_storage_null.e +++ b/modules/auth/persistence/cms_oauth_20_storage_null.e @@ -22,17 +22,17 @@ feature -- Error handler feature -- Access: Users - user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER -- CMS User with Oauth credential by id if any. do end - user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER -- -- CMS User with Oauth credential by access token `a_token' if any. do end - user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL ): detachable CMS_USER do end @@ -40,7 +40,7 @@ feature -- Access: Consumers oauth2_consumers: LIST [STRING] do - create {ARRAYED_LIST[STRING]} Result.make (0) + create {ARRAYED_LIST [STRING]} Result.make (0) end oauth_consumer_by_name (a_name: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER @@ -55,12 +55,12 @@ feature -- Access: Consumers feature -- Change: User Oauth2 - new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) + new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL) -- Add a new user with oauth2 authentication. do end - 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_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL ) -- Update user `a_user' with oauth2 authentication. do end diff --git a/modules/auth/persistence/cms_oauth_20_storage_sql.e b/modules/auth/persistence/cms_oauth_20_storage_sql.e index ffbad07..deba68b 100644 --- a/modules/auth/persistence/cms_oauth_20_storage_sql.e +++ b/modules/auth/persistence/cms_oauth_20_storage_sql.e @@ -22,10 +22,10 @@ create feature -- Access User Outh - user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER - -- Retrieve a user by token `a_token' searching in all the registered consumers in the system. + user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL): detachable CMS_USER + -- Retrieve user by token `a_token' searching in all the registered consumers in the system. local - l_list: LIST[STRING] + l_list: LIST [STRING] do error_handler.reset write_information_log (generator + ".user_oauth2_without_consumer_by_token") @@ -33,16 +33,14 @@ feature -- Access User Outh from l_list.start until - l_list.after or attached Result + l_list.after or Result /= Void loop - if attached {CMS_USER} user_oauth2_by_token (a_token, l_list.item) as l_user then - Result := l_user - end + Result := user_oauth2_by_token (a_token, l_list.item) l_list.forth end end - user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_32): detachable CMS_USER + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER --