diff --git a/examples/demo/demo-safe.ecf b/examples/demo/demo-safe.ecf index 12dc3a7..a30c7f2 100644 --- a/examples/demo/demo-safe.ecf +++ b/examples/demo/demo-safe.ecf @@ -35,10 +35,14 @@ - + + + diff --git a/examples/demo/site/config/cms.ini b/examples/demo/site/config/cms.ini index 41b8ec5..32b9c43 100644 --- a/examples/demo/site/config/cms.ini +++ b/examples/demo/site/config/cms.ini @@ -16,15 +16,16 @@ theme=bootstrap [notification] # By default, notification.email = site.email # you can change here the email that will receive internal messages. -#email=notif@example.com +email=webmaster@example.com [mailer] #The mailer is used mostly used by the CMS to send email messages. # you can change the "From:" by setting mailer.from value" +subject_prefix=[Eiffel CMS] #from=... -#smtp=localhost:25 +smtp=localhost:25 #sendmail=site\bin\roc_sendmail.bat -output=@stderr +output=site\db\mailer.log [modules] # Module status diff --git a/examples/demo/site/modules/auth/config/auth.json b/examples/demo/site/modules/auth/config/auth.json new file mode 100644 index 0000000..728b433 --- /dev/null +++ b/examples/demo/site/modules/auth/config/auth.json @@ -0,0 +1,7 @@ +{ + "subject": "Thank you for contacting us", + "recaptcha": { + "site_key":"6Lex9RMTAAAAAKleC4x6TaRlFcpLbEWgH_U7MSiD", + "secret_key":"6Lex9RMTAAAAAAkBczvX5DUiyg_xoM_EthVVgRRx" + } +} diff --git a/examples/demo/site/modules/auth/files/css/auth.css b/examples/demo/site/modules/auth/files/css/auth.css new file mode 100644 index 0000000..d57de12 --- /dev/null +++ b/examples/demo/site/modules/auth/files/css/auth.css @@ -0,0 +1,28 @@ +ul.cms-temp-users { + list-style-type: none; + padding: 3px 3px 3px 3px; + border: solid 1px #ccc; +} +ul.cms-temp-users li { + border-top: dotted 1px #ccc; +} +ul.cms-temp-users li:first-child { + border-top: none; +} +ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details { + list-style-type: none; + padding: 3px 3px 3px 3px; + border: solid 1px #ccc; +} +ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li { + border-top: dotted 1px #ccc; +} +ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li:first-child { + border-top: none; +} +ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li.cms_temp_user_detail_information::before { + content: "[personal information] "; +} +ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li.cms_temp_user_detail_email::before { + content: "[email] "; +} diff --git a/examples/demo/site/modules/auth/files/scss/auth.scss b/examples/demo/site/modules/auth/files/scss/auth.scss new file mode 100644 index 0000000..38fe7b5 --- /dev/null +++ b/examples/demo/site/modules/auth/files/scss/auth.scss @@ -0,0 +1,37 @@ +ul.cms-temp-users { + + list-style-type: none; + padding: 3px 3px 3px 3px; + border: solid 1px #ccc; + + li{ + border-top: dotted 1px #ccc; + &:first-child { + border-top: none; + } + } + + li.cms_temp_user { + + ul.cms_temp_user_details { + list-style-type: none; + padding: 3px 3px 3px 3px; + border: solid 1px #ccc; + + li{ + border-top: dotted 1px #ccc; + &:first-child { + border-top: none; + } + } + li.cms_temp_user_detail_information::before{ + content: "[personal information] " + } + li.cms_temp_user_detail_email::before{ + content: "[email] " + } + } + } +} + + diff --git a/examples/demo/site/modules/auth/mail_templates/account_activation.html b/examples/demo/site/modules/auth/mail_templates/account_activation.html index 0ab4c4f..5070ce8 100644 --- a/examples/demo/site/modules/auth/mail_templates/account_activation.html +++ b/examples/demo/site/modules/auth/mail_templates/account_activation.html @@ -4,15 +4,10 @@ Activation - + - -

Thank you for registering at ROC CMS

- -

To complete your registration, please click on this link to activate your account:

- -

$link

-

Thank you for joining us.

+

"$user ($email)", thank you for applying to $sitename.

+

We will review your application and send you a resolution.

diff --git a/examples/demo/site/modules/auth/mail_templates/account_activation_confirmation.html b/examples/demo/site/modules/auth/mail_templates/account_activation_confirmation.html new file mode 100644 index 0000000..4dfacf5 --- /dev/null +++ b/examples/demo/site/modules/auth/mail_templates/account_activation_confirmation.html @@ -0,0 +1,13 @@ + + + + + Activation Confirmation + + + + +

Your account "$user ($email)" is confirmed at $sitename.

+

Thank you for joining us.

+ + diff --git a/examples/demo/site/modules/auth/mail_templates/account_new_password.html b/examples/demo/site/modules/auth/mail_templates/account_new_password.html index ad2792d..039268e 100644 --- a/examples/demo/site/modules/auth/mail_templates/account_new_password.html +++ b/examples/demo/site/modules/auth/mail_templates/account_new_password.html @@ -4,14 +4,12 @@ New Password - + - -

You have required a new password at ROC CMS

- -

To complete your request, please click on this link to genereate a new password:

- -

$link

+

You have requested a new password at $sitename.

+

To complete your request, please click on the following link to generate a new password: +

+

diff --git a/examples/demo/site/modules/auth/mail_templates/account_re_activation.html b/examples/demo/site/modules/auth/mail_templates/account_re_activation.html index 3590804..c2fbaec 100644 --- a/examples/demo/site/modules/auth/mail_templates/account_re_activation.html +++ b/examples/demo/site/modules/auth/mail_templates/account_re_activation.html @@ -4,15 +4,14 @@ New Activation - + - -

You have request a new activation token at ROC CMS

+

You have requested a new activation token at $sitename.

-

To complete your registration, please click on this link to activate your account:

- -

$link

+

To complete your registration, please click on the following link to re-activate your account: +

+

Thank you for joining us.

diff --git a/examples/demo/site/modules/auth/mail_templates/account_rejected.html b/examples/demo/site/modules/auth/mail_templates/account_rejected.html new file mode 100644 index 0000000..96cf8bd --- /dev/null +++ b/examples/demo/site/modules/auth/mail_templates/account_rejected.html @@ -0,0 +1,12 @@ + + + + + Application Rejected + + + + +

Your account application is rejected, it was not respecting the requirements from $sitename.

+ + diff --git a/examples/demo/site/modules/auth/mail_templates/account_welcome.html b/examples/demo/site/modules/auth/mail_templates/account_welcome.html index facecee..c4e89d1 100644 --- a/examples/demo/site/modules/auth/mail_templates/account_welcome.html +++ b/examples/demo/site/modules/auth/mail_templates/account_welcome.html @@ -4,10 +4,16 @@ Welcome - + -

Welcome toROC CMS

+

Welcome to $sitename.

+

Your account information: +

    +
  • Email address: "$email" .
  • +
  • User name: "$user" .
  • +
+

Thank you for joining us.

diff --git a/examples/demo/site/modules/auth/mail_templates/admin_account_evaluation.html b/examples/demo/site/modules/auth/mail_templates/admin_account_evaluation.html new file mode 100644 index 0000000..dcecbac --- /dev/null +++ b/examples/demo/site/modules/auth/mail_templates/admin_account_evaluation.html @@ -0,0 +1,26 @@ + + + + + Account Evaluation + + + + + +

Account Evaluation

+

The user $user ($email) wants to register to the site $sitename

+ +

User application:

+

$application

+
+ +

To complete the registration, please click on the following link to activate the user account:

+ +

$activation_url

+ +

To reject the registration, please click on the following link

+ +

$rejection_url

+ + diff --git a/examples/demo/site/modules/auth/scripts/auth_temp_users.sql b/examples/demo/site/modules/auth/scripts/auth_temp_users.sql new file mode 100644 index 0000000..25ad9b5 --- /dev/null +++ b/examples/demo/site/modules/auth/scripts/auth_temp_users.sql @@ -0,0 +1,13 @@ +CREATE TABLE `auth_temp_users` ( + `uid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL, + `name` VARCHAR(100) NOT NULL, + `password` VARCHAR(100) NOT NULL, + `salt` VARCHAR(100) NOT NULL, + `email` VARCHAR(250) NOT NULL, + `application` TEXT NOT NULL, + CONSTRAINT `name` + UNIQUE(`name`) +); + + + diff --git a/examples/demo/site/modules/auth/templates/block_post_reactivate.tpl b/examples/demo/site/modules/auth/templates/block_post_reactivate.tpl index 09e7206..c76cbb5 100644 --- a/examples/demo/site/modules/auth/templates/block_post_reactivate.tpl +++ b/examples/demo/site/modules/auth/templates/block_post_reactivate.tpl @@ -1,3 +1,3 @@
-

We have send you a new activation code, check your email to activate your account.

+

Thanks for your application, we will review it to activate your account.

diff --git a/examples/demo/site/modules/auth/templates/block_post_register.tpl b/examples/demo/site/modules/auth/templates/block_post_register.tpl index d59f75a..c76cbb5 100644 --- a/examples/demo/site/modules/auth/templates/block_post_register.tpl +++ b/examples/demo/site/modules/auth/templates/block_post_register.tpl @@ -1,3 +1,3 @@
-

Thanks for register, check your email to activate your account.

+

Thanks for your application, we will review it to activate your account.

diff --git a/examples/demo/site/modules/auth/templates/block_register.tpl b/examples/demo/site/modules/auth/templates/block_register.tpl index 9e7c478..39104ef 100644 --- a/examples/demo/site/modules/auth/templates/block_register.tpl +++ b/examples/demo/site/modules/auth/templates/block_register.tpl @@ -1,7 +1,7 @@
- Register Form + Registration
@@ -20,8 +20,19 @@ {$error_email/}
{/if}
- - +
+ + + {if isset="$error_application"} + {$error_application/}
+ {/if} +
+ {unless isempty="$recaptcha_site_key"} +
+
+ {/unless}
diff --git a/examples/demo/site/scripts/user.sql b/examples/demo/site/scripts/user.sql index 03c7166..161642d 100644 --- a/examples/demo/site/scripts/user.sql +++ b/examples/demo/site/scripts/user.sql @@ -46,3 +46,18 @@ CREATE TABLE `users_password_recovery` ( CONSTRAINT `token` UNIQUE (`token`) ); + +CREATE TABLE `auth_temp_users` ( + `uid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL, + `name` VARCHAR(100) NOT NULL, + `password` VARCHAR(100) NOT NULL, + `salt` VARCHAR(100) NOT NULL, + `email` VARCHAR(250) NOT NULL, + `application` TEXT NOT NULL, + CONSTRAINT `name` + UNIQUE(`name`) +); + + + + diff --git a/library/email/email_service.e b/library/email/email_service.e index 1e1c19b..03073a7 100644 --- a/library/email/email_service.e +++ b/library/email/email_service.e @@ -35,12 +35,14 @@ feature {NONE} -- Initialization parameters: EMAIL_SERVICE_PARAMETERS -- Associated parameters. - admin_email: IMMUTABLE_STRING_8 - -- Site admin's email. - mailer: NOTIFICATION_MAILER -- SMTP protocol. +feature -- Access + + admin_email: IMMUTABLE_STRING_8 + -- Site admin's email. + feature -- Basic Operations send_internal_email (a_content: READABLE_STRING_GENERAL) diff --git a/library/model/src/user/cms_temp_user.e b/library/model/src/user/cms_temp_user.e new file mode 100644 index 0000000..21926a2 --- /dev/null +++ b/library/model/src/user/cms_temp_user.e @@ -0,0 +1,45 @@ +note + description: "User for temporary account." + date: "$Date$" + revision: "$Revision$" + +class + CMS_TEMP_USER + +inherit + CMS_USER + +create + make, + make_with_id + +feature -- Access + + personal_information: detachable STRING_32 + -- User personal information. + + salt: detachable STRING_32 + -- User's password salt. + +feature -- Element change + + set_personal_information (a_personal_information: like personal_information) + -- Assign `personal_information' with `a_personal_information'. + do + personal_information := a_personal_information + ensure + personal_information_assigned: personal_information = a_personal_information + end + + set_salt (a_salt: like salt) + -- Assign `salt' with `a_salt'. + do + salt := a_salt + ensure + salt_assigned: salt = a_salt + end + +note + copyright: "2011-2016, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/library/model/src/user/cms_user.e b/library/model/src/user/cms_user.e index b0dfbec..761abe0 100644 --- a/library/model/src/user/cms_user.e +++ b/library/model/src/user/cms_user.e @@ -62,7 +62,7 @@ feature -- Access hashed_password: detachable READABLE_STRING_8 -- Hashed user password. - email: detachable READABLE_STRING_32 + email: detachable READABLE_STRING_8 -- User email. profile: detachable CMS_USER_PROFILE @@ -80,7 +80,6 @@ feature -- Access -- active -- trashed - feature -- Access: helper utf_8_name: STRING_8 @@ -302,6 +301,6 @@ invariant id_or_name_set: id > 0 or else not name.is_whitespace note - copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" + copyright: "2011-2016, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/library/recaptcha/Readme.md b/library/recaptcha/Readme.md new file mode 100644 index 0000000..aaa8522 --- /dev/null +++ b/library/recaptcha/Readme.md @@ -0,0 +1,4 @@ +Recaptcha Eiffel Lbrary + +Based on https://developers.google.com/recaptcha/ + diff --git a/library/recaptcha/license.lic b/library/recaptcha/license.lic new file mode 100644 index 0000000..93c113a --- /dev/null +++ b/library/recaptcha/license.lic @@ -0,0 +1,10 @@ +${NOTE_KEYWORD} + copyright: "2011-${YEAR} Javier Velilla, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" diff --git a/library/recaptcha/recaptcha-safe.ecf b/library/recaptcha/recaptcha-safe.ecf new file mode 100644 index 0000000..2d950a8 --- /dev/null +++ b/library/recaptcha/recaptcha-safe.ecf @@ -0,0 +1,20 @@ + + + + + + /.git$ + /EIFGENs$ + /CVS$ + /.svn$ + + + + + + + + + diff --git a/library/recaptcha/recaptcha.ecf b/library/recaptcha/recaptcha.ecf new file mode 100644 index 0000000..47d123d --- /dev/null +++ b/library/recaptcha/recaptcha.ecf @@ -0,0 +1,22 @@ + + + + + + /.git$ + /EIFGENs$ + /CVS$ + /.svn$ + + + + + + + + + + + diff --git a/library/recaptcha/src/recaptcha_api.e b/library/recaptcha/src/recaptcha_api.e new file mode 100644 index 0000000..1149a4b --- /dev/null +++ b/library/recaptcha/src/recaptcha_api.e @@ -0,0 +1,147 @@ +note + description: "[ + Simple API to call {RECAPTCHA} Google API. + Example call: + https://www.google.com/recaptcha/api/siteverify?secret=your_secret&response=response_string&remoteip=user_ip_address + ]" + date: "$Date: 2015-01-28 11:44:15 -0300 (mi. 28 de ene. de 2015) $" + revision: "$Revision: 96551 $" + EIS: "name=RECAPTCHA", "src=https://developers.google.com/recaptcha/", "protocol=uri" + EIS: "name=RECAPTCHA API verify", "src=https://developers.google.com/recaptcha/docs/verify", "protocol=uri" + +class + RECAPTCHA_API + +create + make + +feature {NONE} -- Initialization + + make (a_secret_key, a_response: READABLE_STRING_8) + -- Create an object Recaptcha with secret key `a_secret_key' and response token `a_response'. + do + secret := a_secret_key + response := a_response + ensure + secret_set: secret.same_string (a_secret_key) + response_set: response.same_string (a_response) + end + +feature -- Access + + base_uri: STRING_8 = "https://www.google.com/recaptcha/api/siteverify" + -- Recaptcha base URI + + secret: READABLE_STRING_8 + -- Required. The shared key between your site and ReCAPTCHA. + + response: READABLE_STRING_8 + -- Required. The user response token provided by the reCAPTCHA to the user and provided to your site on. + + remoteip: detachable READABLE_STRING_8 + -- Optional. The user's IP address. + +feature -- Status Reports + + errors: detachable LIST [READABLE_STRING_8] + -- optional table of error codes + -- missing-input-secret The secret parameter is missing. + -- invalid-input-secret The secret parameter is invalid or malformed. + -- missing-input-response The response parameter is missing. + -- invalid-input-response The response parameter is invalid or malformed. + +feature -- Change Element + + set_remoteip (a_remoteip: READABLE_STRING_8) + -- Set `remoteip' with `a_remoteip'. + do + remoteip := a_remoteip + ensure + remoteip_set: remoteip = a_remoteip + end + +feature -- API + + verify: BOOLEAN + -- Verify the user's response + local + l_parser: JSON_PARSER + do + if attached get as l_response then + if attached l_response.body as l_body then + create l_parser.make_with_string (l_body) + l_parser.parse_content + if + l_parser.is_parsed and then attached {JSON_OBJECT} l_parser.parsed_json_object as jv and then + attached {JSON_BOOLEAN} jv.item ("success") as l_success + then + Result := l_success.item + if not Result and then attached {JSON_ARRAY} jv.item ("error-codes") as l_error_codes then + across + l_error_codes as c + loop + if attached {JSON_STRING} c.item as ji then + put_error (ji.unescaped_string_32) + end + end + end + end + else + put_error (l_response.status.out) + end + else + put_error ("unknown") + end + end + +feature {NONE} -- REST API + + get: detachable RESPONSE + -- Reading Data + local + l_request: REQUEST + do + create l_request.make ("GET", new_uri) + Result := l_request.execute + end + +feature {NONE} -- Implementation + + new_uri: STRING_8 + -- new uri (BaseUri?secret=secret_value&response=response_value[&remoteip=remoteip_value] + do + create Result.make_from_string (base_uri) + Result.append ("?secret=") + Result.append (secret) + Result.append ("&response=") + Result.append (response) + if attached remoteip as l_remoteip then + Result.append ("&remoteip=" + l_remoteip) + end + end + + put_error (a_code: READABLE_STRING_GENERAL) + local + l_errors: like errors + utf: UTF_CONVERTER + do + l_errors := errors + if l_errors = Void then + create {ARRAYED_LIST [STRING]} l_errors.make (1) + errors := l_errors + end + l_errors.force (utf.utf_32_string_to_utf_8_string_8 (a_code)) + end + +note + copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" + +end diff --git a/library/recaptcha/test/application.e b/library/recaptcha/test/application.e new file mode 100644 index 0000000..2e0e25b --- /dev/null +++ b/library/recaptcha/test/application.e @@ -0,0 +1,61 @@ +note + description : "test application root class" + date : "$Date: 2015-01-14 15:37:57 -0300 (mi. 14 de ene. de 2015) $" + revision : "$Revision: 96458 $" + +class + APPLICATION + +inherit + ARGUMENTS + +create + make + +feature {NONE} -- Initialization + + make + -- Run application. + do + test_invalid_input + test_missing_input + test_missing_key_input + end + + + test_invalid_input + -- invalid-input-response + local + l_captcha: RECAPTCHA_API + do + create l_captcha.make ("","234") + check + not_true:not l_captcha.verify + end + end + + test_missing_input + -- missing-input-response + local + l_captcha: RECAPTCHA_API + do + create l_captcha.make ("key","") + check + not_true:not l_captcha.verify + end + end + + test_missing_key_input + -- missing-input-response + -- invalid-input-response + local + l_captcha: RECAPTCHA_API + do + create l_captcha.make ("","") + l_captcha.set_remoteip("localhost") + check + not_true:not l_captcha.verify + end + end + +end diff --git a/library/recaptcha/test/recaptcha_api_test_set.e b/library/recaptcha/test/recaptcha_api_test_set.e new file mode 100644 index 0000000..4deb220 --- /dev/null +++ b/library/recaptcha/test/recaptcha_api_test_set.e @@ -0,0 +1,69 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date: 2015-01-14 15:37:57 -0300 (mi. 14 de ene. de 2015) $" + revision: "$Revision: 96458 $" + testing: "type/manual" + +class + RECAPTCHA_API_TEST_SET + +inherit + EQA_TEST_SET + +feature -- Test routines + + test_invalid_input + -- invalid-input-response + local + l_captcha: RECAPTCHA_API + do + create l_captcha.make ("","234") + check + not_true:not l_captcha.verify + end + assert ("Not true", not l_captcha.verify) + assert ("Has error invalid-input-response",has_error (l_captcha,"invalid-input-response")) + end + + test_missing_input + -- missing-input-response + local + l_captcha: RECAPTCHA_API + do + create l_captcha.make ("key","") + check + not_true:not l_captcha.verify + end + assert ("Not true", not l_captcha.verify) + assert ("Has error missing-input-response",has_error (l_captcha,"missing-input-response")) + end + + test_missing_key_input + -- missing-input-response + -- invalid-input-response + local + l_captcha: RECAPTCHA_API + do + create l_captcha.make ("","") + l_captcha.set_remoteip("localhost") + assert ("Not true", not l_captcha.verify) + assert ("Has error missing-input-response",has_error (l_captcha,"missing-input-response")) + assert ("Has error invalid-input-response",has_error (l_captcha,"invalid-input-response")) + end + +feature {NONE} -- Implementation + + has_error (l_captcha: RECAPTCHA_API; a_error: READABLE_STRING_32): BOOLEAN + do + if attached l_captcha.errors as l_errors then + l_errors.compare_objects + Result := l_errors.has (a_error) + end + end + +end + + diff --git a/library/recaptcha/test/test.ecf b/library/recaptcha/test/test.ecf new file mode 100644 index 0000000..879c446 --- /dev/null +++ b/library/recaptcha/test/test.ecf @@ -0,0 +1,21 @@ + + + + + + + + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + diff --git a/modules/admin/handler/user/cms_user_view_response.e b/modules/admin/handler/user/cms_user_view_response.e index 4cf3716..930cf20 100644 --- a/modules/admin/handler/user/cms_user_view_response.e +++ b/modules/admin/handler/user/cms_user_view_response.e @@ -73,7 +73,7 @@ feature -- Execution s.append ("
") s.append ("

Account Information

") s.append ("

Username: ") - s.append (a_user.name) + s.append (html_encoded (a_user.name)) s.append ("

") if attached a_user.email as l_email then s.append ("

Email: ") diff --git a/modules/auth/auth-safe.ecf b/modules/auth/auth-safe.ecf index c73df30..16a2cb4 100644 --- a/modules/auth/auth-safe.ecf +++ b/modules/auth/auth-safe.ecf @@ -1,32 +1,31 @@ - + /.git$ - /EIFGENs$ /.svn$ + /EIFGENs$ - + + + + + + - - - - - - - diff --git a/modules/auth/cms_authentication_email_service.e b/modules/auth/cms_authentication_email_service.e index c95a1d0..86d2455 100644 --- a/modules/auth/cms_authentication_email_service.e +++ b/modules/auth/cms_authentication_email_service.e @@ -81,51 +81,110 @@ feature -- Basic Operations / Internal feature -- Basic Operations / Contact - send_contact_email (a_to, a_content: READABLE_STRING_8) - -- Send successful contact message `a_token' to `a_to'. + send_account_evaluation (a_user: CMS_USER; a_application, a_url_activate, a_url_reject, a_host: READABLE_STRING_8) + -- Send new user register to webmaster to confirm or reject itt. + local + l_message: STRING + do + create l_message.make_from_string (parameters.account_evaluation) + l_message.replace_substring_all ("$host", a_host) + l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name) + l_message.replace_substring_all ("$user", a_user.utf_8_name) + if attached a_user.email as l_email then + l_message.replace_substring_all ("$email", l_email) + else + l_message.replace_substring_all ("$email", "unknown email") + end + l_message.replace_substring_all ("$application", a_application) + l_message.replace_substring_all ("$activation_url", a_url_activate) + l_message.replace_substring_all ("$rejection_url", a_url_reject) + send_message (contact_email_address, contact_email_address, parameters.contact_subject_account_evaluation, l_message) + end + + send_contact_email (a_to: READABLE_STRING_8; a_user: CMS_USER; a_host: READABLE_STRING_8) + -- Send successful contact message for user `a_user' to `a_to'. require attached_to: a_to /= Void local l_message: STRING do create l_message.make_from_string (parameters.account_activation) - l_message.replace_substring_all ("$link", a_content) + l_message.replace_substring_all ("$host", a_host) + l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name) + l_message.replace_substring_all ("$user", a_user.utf_8_name) send_message (contact_email_address, a_to, parameters.contact_subject_register, l_message) end - send_contact_activation_email (a_to, a_content: READABLE_STRING_8) - -- Send successful contact message `a_token' to `a_to'. + send_contact_activation_email (a_to: READABLE_STRING_8; a_user: CMS_USER; a_link, a_host: READABLE_STRING_8) + -- Send successful message activation to `a_to'. require attached_to: a_to /= Void local l_message: STRING do create l_message.make_from_string (parameters.account_re_activation) - l_message.replace_substring_all ("$link", a_content) + l_message.replace_substring_all ("$host", a_host) + l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name) + l_message.replace_substring_all ("$link", a_link) send_message (contact_email_address, a_to, parameters.contact_subject_activate, l_message) end - send_contact_password_email (a_to, a_content: READABLE_STRING_8) - -- Send successful contact message `a_token' to `a_to'. + send_contact_activation_confirmation_email (a_to: READABLE_STRING_8; a_user: CMS_USER; a_host: READABLE_STRING_8) + -- Send successful message activation to a_to. + require + attached_to: a_to /= Void + local + l_message: STRING + do + create l_message.make_from_string (parameters.account_activation_confirmation) + l_message.replace_substring_all ("$hot", a_host) + l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name) + l_message.replace_substring_all ("$user", a_user.utf_8_name) + l_message.replace_substring_all ("$email", a_to) + send_message (contact_email_address, a_to, parameters.contact_subject_activated, l_message) + end + + send_contact_activation_reject_email (a_to: READABLE_STRING_8; a_user: CMS_USER; a_host: READABLE_STRING_8) + -- Send successful contact activation reject message to `a_to'. + require + attached_to: a_to /= Void + local + l_message: STRING + do + create l_message.make_from_string (parameters.account_rejected) + l_message.replace_substring_all ("$host", a_host) + l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name) + l_message.replace_substring_all ("$email", a_to) + l_message.replace_substring_all ("$user", a_user.utf_8_name) + send_message (contact_email_address, a_to, parameters.contact_subject_rejected, l_message) + end + + send_contact_password_email (a_to: READABLE_STRING_8; a_user: CMS_USER; a_link, a_host: READABLE_STRING_8) + -- Send successful new account password message to `a_to'. require attached_to: a_to /= Void local l_message: STRING do create l_message.make_from_string (parameters.account_password) - l_message.replace_substring_all ("$link", a_content) + l_message.replace_substring_all ("$host", a_host) + l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name) + l_message.replace_substring_all ("$link", a_link) send_message (contact_email_address, a_to, parameters.contact_subject_password, l_message) end - send_contact_welcome_email (a_to, a_content: READABLE_STRING_8) - -- Send successful contact message `a_token' to `a_to'. + send_contact_welcome_email (a_to: READABLE_STRING_8; a_user: CMS_USER; a_host: READABLE_STRING_8) + -- Send successful welcome message to `a_to'. require attached_to: a_to /= Void local l_message: STRING do create l_message.make_from_string (parameters.account_welcome) - l_message.replace_substring_all ("$link", a_content) + l_message.replace_substring_all ("$host", a_host) + l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name) + l_message.replace_substring_all ("$email", a_to) + l_message.replace_substring_all ("$user", a_user.utf_8_name) send_message (contact_email_address, a_to, parameters.contact_subject_oauth, l_message) end diff --git a/modules/auth/cms_authentication_email_service_parameters.e b/modules/auth/cms_authentication_email_service_parameters.e index 51ce516..83bbe7b 100644 --- a/modules/auth/cms_authentication_email_service_parameters.e +++ b/modules/auth/cms_authentication_email_service_parameters.e @@ -14,18 +14,18 @@ feature {NONE} -- Initialization make (a_cms_api: CMS_API) local utf: UTF_CONVERTER - l_site_name: READABLE_STRING_8 s: detachable READABLE_STRING_32 + l_utf8_site_name: IMMUTABLE_STRING_8 l_contact_email, l_subject_register, l_subject_activate, l_subject_password, l_subject_oauth: detachable READABLE_STRING_8 do cms_api := a_cms_api - -- Use global smtp setting if any, otherwise "localhost" - l_site_name := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.site_name) + create l_utf8_site_name.make_from_string (a_cms_api.setup.utf_8_site_name) + utf_8_site_name := l_utf8_site_name notif_email_address := a_cms_api.setup.site_notification_email sender_email_address := a_cms_api.setup.site_email if not notif_email_address.has ('<') then - notif_email_address := l_site_name + " <" + notif_email_address + ">" + notif_email_address := l_utf8_site_name + " <" + notif_email_address + ">" end if attached a_cms_api.module_configuration_by_name ({CMS_AUTHENTICATION_MODULE}.name, Void) as cfg then @@ -50,14 +50,14 @@ feature {NONE} -- Initialization l_subject_oauth := utf.utf_32_string_to_utf_8_string_8 (s) end end - if l_contact_email /= Void then - if not l_contact_email.has ('<') then - l_contact_email := l_site_name + " <" + l_contact_email + ">" - end - contact_email_address := l_contact_email - else - contact_email_address := notif_email_address + if l_contact_email = Void then + l_contact_email := notif_email_address end + if not l_contact_email.has ('<') then + l_contact_email := l_utf8_site_name + " <" + l_contact_email + ">" + end + contact_email_address := l_contact_email + if l_subject_register /= Void then contact_subject_register := l_subject_register else @@ -79,8 +79,14 @@ feature {NONE} -- Initialization else contact_subject_oauth := "Welcome." end + + contact_subject_account_evaluation := "New register, account evalution." + contact_subject_rejected := "Your account was rejected." + contact_subject_activated := "Your account was activated." end + + feature -- Access cms_api: CMS_API @@ -92,10 +98,22 @@ feature -- Access contact_email_address: IMMUTABLE_STRING_8 -- Contact email. + utf_8_site_name: IMMUTABLE_STRING_8 + -- UTF-8 encoded Site name. + + contact_subject_account_evaluation: IMMUTABLE_STRING_8 contact_subject_register: IMMUTABLE_STRING_8 contact_subject_activate: IMMUTABLE_STRING_8 contact_subject_password: IMMUTABLE_STRING_8 contact_subject_oauth: IMMUTABLE_STRING_8 + contact_subject_rejected: IMMUTABLE_STRING_8 + contact_subject_activated: IMMUTABLE_STRING_8 + + account_evaluation: STRING + -- Account evaluation template email message. + do + Result := template_string ("admin_account_evaluation.html", default_template_account_evaluation) + end account_activation: STRING -- Account activation template email message. @@ -103,12 +121,24 @@ feature -- Access Result := template_string ("account_activation.html", default_template_account_activation) end + account_activation_confirmation: STRING + -- Account activation confirmation template email message. + do + Result := template_string ("account_activation_confirmation.html", default_template_account_activation_confirmation) + end + account_re_activation: STRING -- Account re_activation template email message. do Result := template_string ("accunt_re_activation.html", default_template_account_re_activation) end + account_rejected: STRING + -- Account rejected template email message. + do + Result := template_string ("accunt_rejected.html", default_template_account_rejected) + end + account_password: STRING -- Account password template email message. do @@ -137,7 +167,7 @@ feature {NONE} -- Implementation: Template local p: PATH do - p := template_path ("account_activation.html") + p := template_path (a_name) if attached read_template_file (p) as l_content then Result := l_content else @@ -168,6 +198,36 @@ feature {NONE} -- Implementation feature {NONE} -- Message email + default_template_account_evaluation: STRING = "[ + + + + + Account Evaluation + + + + + +

Account Evaluation

+

The user $user ($email) wants to register to the site $sitename

+ +

This is his/her application.

+

$application

+
+ +

To complete the registration, please click on the following link to activate the user account:

+ +

$activation_url

+ +

To reject the registration, please click on the following link

+ +

$rejection_url

+ + + ]" + + default_template_account_activation: STRING = "[ @@ -175,21 +235,53 @@ feature {NONE} -- Message email Activation - + -

Thank you for registering at ROC CMS

+

Thank you for applying to $sitename $user

-

To complete your registration, please click on the following link to activate your account:

- -

$link

+

We will review your application and send you an email

Thank you for joining us.

]" + default_template_account_activation_confirmation: STRING = "[ + + + + + Activation + + + + + +

Your account has been confirmed $sitename $email

+ +

Thank you for joining us.

+ + + ]" + + default_template_account_rejected: STRING = "[ + + + + + Application Rejected + + + + + +

You requested has been rejected, your application does not conform our rules $sitename

+ + + ]" + default_template_account_re_activation: STRING = "[ @@ -197,11 +289,11 @@ feature {NONE} -- Message email New Activation - + -

You have requested a new activation token at ROC CMS

+

You have requested a new activation token at $sitename

To complete your registration, please click on the following link to activate your account:

@@ -220,11 +312,11 @@ feature {NONE} -- Message email New Password - + -

You have required a new password at ROC CMS

+

You have required a new password at $sitename

To complete your request, please click on this link to generate a new password:

@@ -241,11 +333,11 @@ feature {NONE} -- Message email Welcome - + -

Welcome toROC CMS

+

Welcome to$sitename

Thank you for joining us.

diff --git a/modules/auth/cms_authentication_module.e b/modules/auth/cms_authentication_module.e index a858425..b2b7aea 100644 --- a/modules/auth/cms_authentication_module.e +++ b/modules/auth/cms_authentication_module.e @@ -7,6 +7,7 @@ class CMS_AUTHENTICATION_MODULE inherit + CMS_MODULE redefine setup_hooks, @@ -15,6 +16,8 @@ inherit CMS_HOOK_AUTO_REGISTER + CMS_HOOK_RESPONSE_ALTER + CMS_HOOK_VALUE_TABLE_ALTER CMS_HOOK_BLOCK @@ -43,7 +46,6 @@ feature {NONE} -- Initialization version := "1.0" description := "Authentication module" package := "authentication" - create root_dir.make_current cache_duration := 0 end @@ -57,6 +59,10 @@ feature -- Access do Result := Precursor Result.force ("account register") + Result.force ("account activate") + Result.force ("account reject") + Result.force ("account reactivate") + Result.force ("admin registration") end feature -- Access: docs @@ -80,20 +86,29 @@ feature -- Router -- do configure_web (a_api, a_router) + configure_web_admin (a_api, a_router) end configure_web (a_api: CMS_API; a_router: WSF_ROUTER) do - a_router.handle ("/account", create {WSF_URI_AGENT_HANDLER}.make (agent handle_account (a_api, ?, ?)), a_router.methods_head_get) - a_router.handle ("/account/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get) - a_router.handle ("/account/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_head_get) - a_router.handle ("/account/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle ("/account/activate/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_activation (a_api, ?, ?)), a_router.methods_head_get) - a_router.handle ("/account/reactivate", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reactivation (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle ("/account/change-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_change_password (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle ("/account/post-change-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_post_change_password (a_api, ?, ?)), a_router.methods_get) + a_router.handle ("/account", create {WSF_URI_AGENT_HANDLER}.make (agent handle_account(a_api, ?, ?)), a_router.methods_head_get) + a_router.handle ("/account/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login(a_api, ?, ?)), a_router.methods_head_get) + a_router.handle ("/account/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout(a_api, ?, ?)), a_router.methods_head_get) + a_router.handle ("/account/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register(a_api, ?, ?)), a_router.methods_get_post) + a_router.handle ("/account/activate/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_activation(a_api, ?, ?)), a_router.methods_head_get) + a_router.handle ("/account/reject/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_reject(a_api, ?, ?)), a_router.methods_head_get) + a_router.handle ("/account/reactivate", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reactivation(a_api, ?, ?)), a_router.methods_get_post) + a_router.handle ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password(a_api, ?, ?)), a_router.methods_get_post) + a_router.handle ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password(a_api, ?, ?)), a_router.methods_get_post) + a_router.handle ("/account/change-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_change_password(a_api, ?, ?)), a_router.methods_get_post) + a_router.handle ("/account/post-change-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_post_change_password(a_api, ?, ?)), a_router.methods_get) + end + + + configure_web_admin (a_api: CMS_API; a_router: WSF_ROUTER) + -- Configure router mapping for admin web interface. + do + a_router.handle ("/admin/pending-registrations/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_admin_pending_registrations (?, ?, a_api)), a_router.methods_get) end feature -- Hooks configuration @@ -104,6 +119,7 @@ feature -- Hooks configuration auto_subscribe_to_hooks (a_hooks) a_hooks.subscribe_to_block_hook (Current) a_hooks.subscribe_to_value_table_alter_hook (Current) + a_hooks.subscribe_to_menu_system_alter_hook (Current) end value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE) @@ -119,7 +135,7 @@ feature -- Hooks configuration lnk: CMS_LOCAL_LINK do if attached a_response.user as u then - create lnk.make (u.name, "account" ) + create lnk.make (u.name, "account") lnk.set_weight (97) a_menu_system.primary_menu.extend (lnk) create lnk.make ("Logout", "account/roc-logout") @@ -130,7 +146,11 @@ feature -- Hooks configuration lnk.set_weight (98) a_menu_system.primary_menu.extend (lnk) end - + -- Add the link to the taxonomy to the main menu + if a_response.has_permission ("admin registration") then + create lnk.make ("Registration", "admin/pending-registrations/") + a_menu_system.management_menu.extend (lnk) + end end feature -- Handler @@ -140,7 +160,6 @@ feature -- Handler r: CMS_RESPONSE do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) - if attached template_block ("account_info", r) as l_tpl_block then if attached r.user as l_user then r.set_value (api.user_api.user_roles (l_user), "roles") @@ -182,11 +201,14 @@ feature -- Handler local r: CMS_RESPONSE l_user_api: CMS_USER_API - u: CMS_USER + u: CMS_TEMP_USER l_exist: BOOLEAN es: CMS_AUTHENTICATION_EMAIL_SERVICE - l_url: STRING + l_url_activate: STRING + l_url_reject: STRING l_token: STRING + l_captcha_passed: BOOLEAN + l_email: READABLE_STRING_8 do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) if r.has_permission ("account register") then @@ -194,50 +216,77 @@ feature -- Handler if attached {WSF_STRING} req.form_parameter ("name") as l_name and then attached {WSF_STRING} req.form_parameter ("password") as l_password and then - attached {WSF_STRING} req.form_parameter ("email") as l_email + attached {WSF_STRING} req.form_parameter ("email") as p_email and then + attached {WSF_STRING} req.form_parameter ("personal_information") as l_personal_information then - l_user_api := api.user_api + if p_email.value.is_valid_as_string_8 then + l_email := p_email.value.to_string_8 + l_user_api := api.user_api + if attached l_user_api.user_by_name (l_name.value) or else attached l_user_api.temp_user_by_name (l_name.value) then + -- Username already exist. + r.set_value ("User name already exists!", "error_name") + l_exist := True + end + if attached l_user_api.user_by_email (l_email) or else attached l_user_api.temp_user_by_email (l_email) then + -- Emails already exist. + r.set_value ("An account is already associated with that email address!", "error_email") + l_exist := True + end + if attached recaptcha_secret_key (api) as l_recaptcha_key then + if attached {WSF_STRING} req.form_parameter ("g-recaptcha-response") as l_recaptcha_response and then is_captcha_verified (l_recaptcha_key, l_recaptcha_response.value) then + l_captcha_passed := True + else + --| Bad or missing captcha + l_captcha_passed := False + end + else + --| reCaptcha is not setup, so no verification + l_captcha_passed := True + end + if not l_exist then + -- New temp user + create u.make (l_name.value) + u.set_email (l_email) + u.set_password (l_password.value) + u.set_personal_information (l_personal_information.value) + l_user_api.new_temp_user (u) - if attached l_user_api.user_by_name (l_name.value) then - -- Username already exist. - r.set_value ("User name already exists!", "error_name") - l_exist := True - end - if attached l_user_api.user_by_email (l_email.value) then - -- Emails already exist. - r.set_value ("An account is already associated with that email address!", "error_email") - l_exist := True - end + -- Create activation token + l_token := new_token + l_user_api.new_activation (l_token, u.id) + l_url_activate := req.absolute_script_url ("/account/activate/" + l_token) + l_url_reject := req.absolute_script_url ("/account/reject/" + l_token) - if not l_exist then - -- New user - create u.make (l_name.value) - u.set_email (l_email.value) - u.set_password (l_password.value) - l_user_api.new_user (u) - - -- Create activation token - l_token := new_token - l_user_api.new_activation (l_token, u.id) - 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_url) + -- Send Email to webmaster + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) + write_debug_log (generator + ".handle register: send_register_email") + es.send_account_evaluation (u, l_personal_information.value, l_url_activate, l_url_reject, req.absolute_script_url ("")) + -- Send Email to user + 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, u, req.absolute_script_url ("")) + else + r.set_value (l_name.value, "name") + r.set_value (l_email, "email") + r.set_value (l_personal_information.value, "personal_information") + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + end else r.set_value (l_name.value, "name") - r.set_value (l_email.value, "email") + r.set_value (p_email.value, "email") + r.set_value (l_personal_information.value, "personal_information") r.set_status_code ({HTTP_CONSTANTS}.bad_request) end + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.set_main_content ("There were issue with your application, invalid or missing values.") end end else create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api) r.set_main_content ("You can also contact the webmaster to ask for an account.") end - r.execute end @@ -246,29 +295,84 @@ feature -- Handler r: CMS_RESPONSE l_user_api: CMS_USER_API l_ir: INTERNAL_SERVER_ERROR_CMS_RESPONSE + es: CMS_AUTHENTICATION_EMAIL_SERVICE do l_user_api := api.user_api create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) - if attached {WSF_STRING} req.path_parameter ("token") as l_token then + if r.has_permission ("account activate") then + if attached {WSF_STRING} req.path_parameter ("token") as l_token then + if attached {CMS_TEMP_USER} l_user_api.temp_user_by_activation_token (l_token.value) as l_user then - if attached {CMS_USER} l_user_api.user_by_activation_token (l_token.value) as l_user then - -- Valid user_id - l_user.mark_active - l_user_api.update_user (l_user) - l_user_api.remove_activation (l_token.value) - r.set_main_content ("

Your account "+ l_user.name +" has been activated

") + -- TODO copy the personal information + --! to CMS_USER_PROFILE and persist data + --! check also CMS_USER.data_items + + -- Delete temporal User + l_user_api.delete_temp_user (l_user) + + -- Valid user_id + l_user.set_id (0) + l_user.mark_active + l_user_api.new_user_from_temp_user (l_user) + l_user_api.remove_activation (l_token.value) + r.set_main_content ("

The account " + html_encoded (l_user.name) + " has been activated

") + -- Send Email + if attached l_user.email as l_email then + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) + write_debug_log (generator + ".handle register: send_contact_activation_confirmation_email") + es.send_contact_activation_confirmation_email (l_email, l_user, req.absolute_script_url ("")) + end + else + -- the token does not exist, or it was already used. + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + r.set_main_content ("

The token " + l_token.value + " is not valid " + r.link ("Reactivate Account", "account/reactivate", Void) + "

") + end + r.execute else - -- the token does not exist, or it was already used. - r.set_status_code ({HTTP_CONSTANTS}.bad_request) - r.set_main_content ("

The token " + l_token.value +" is not valid " + r.link ("Reactivate Account", "account/reactivate", Void) + "

") + create l_ir.make (req, res, api) + l_ir.execute end - r.execute else - create l_ir.make (req, res, api) - l_ir.execute + create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.execute end end + handle_reject (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + es: CMS_AUTHENTICATION_EMAIL_SERVICE + l_ir: INTERNAL_SERVER_ERROR_CMS_RESPONSE + l_user_api: CMS_USER_API + do + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + if r.has_permission ("account reject") then + if attached {WSF_STRING} req.path_parameter ("token") as l_token then + l_user_api := api.user_api + if attached {CMS_TEMP_USER} l_user_api.temp_user_by_activation_token (l_token.value) as l_user then + l_user_api.delete_temp_user (l_user) + r.set_main_content ("

The temporal account for " + html_encoded (l_user.name) + " has been removed

") + -- Send Email + if attached l_user.email as l_email then + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) + write_debug_log (generator + ".handle register: send_contact_activation_reject_email") + es.send_contact_activation_reject_email (l_email, l_user, req.absolute_script_url ("")) + end + else + -- the token does not exist, or it was already used. + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + r.set_main_content ("

The token " + l_token.value + " is not valid ") + end + r.execute + else + create l_ir.make (req, res, api) + l_ir.execute + end + else + create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.execute + end + end handle_reactivation (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local @@ -276,37 +380,50 @@ feature -- Handler es: CMS_AUTHENTICATION_EMAIL_SERVICE l_user_api: CMS_USER_API l_token: STRING - l_url: STRING + l_url_activate: STRING + l_url_reject: STRING + l_email: READABLE_STRING_8 do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) - if req.is_post_request_method then - if - attached {WSF_STRING} req.form_parameter ("email") as l_email - then - l_user_api := api.user_api - if attached {CMS_USER} l_user_api.user_by_email (l_email.value) as l_user then - -- User exist create a new token and send a new email. - if l_user.is_active then - r.set_value ("The asociated user to the given email " + l_email.value + " , is already active", "is_active") - r.set_status_code ({HTTP_CONSTANTS}.bad_request) + if r.has_permission ("account reactivate") then + if req.is_post_request_method then + if attached {WSF_STRING} req.form_parameter ("email") as p_email then + if p_email.value.is_valid_as_string_8 then + l_email := p_email.value.to_string_8 + l_user_api := api.user_api + if attached {CMS_TEMP_USER} l_user_api.temp_user_by_email (l_email) as l_user then + -- User exist create a new token and send a new email. + if l_user.is_active then + r.set_value ("The asociated user to the given email " + l_email + " , is already active", "is_active") + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + else + l_token := new_token + l_user_api.new_activation (l_token, l_user.id) + l_url_activate := req.absolute_script_url ("/account/activate/" + l_token) + l_url_reject := req.absolute_script_url ("/account/reject/" + l_token) + -- Send Email to webmaster + if attached l_user.personal_information as l_personal_information then + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) + write_debug_log (generator + ".handle register: send_register_email") + es.send_account_evaluation (l_user, l_personal_information, l_url_activate, l_url_reject, req.absolute_script_url ("")) + end + end + else + r.set_value ("The email does not exist !", "error_email") + r.set_value (l_email, "email") + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + end else - l_token := new_token - l_user_api.new_activation (l_token, l_user.id) - 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_url) + r.set_value ("The email is not valid!", "error_email") + r.set_value (p_email.value, "email") + r.set_status_code ({HTTP_CONSTANTS}.bad_request) end - else - r.set_value ("The email does not exist or !", "error_email") - r.set_value (l_email.value, "email") - r.set_status_code ({HTTP_CONSTANTS}.bad_request) end end + else + create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.execute end - r.execute end @@ -317,39 +434,48 @@ feature -- Handler l_user_api: CMS_USER_API l_token: STRING l_url: STRING + l_email: READABLE_STRING_8 do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) if req.is_post_request_method then l_user_api := api.user_api - if attached {WSF_STRING} req.form_parameter ("email") as l_email then - if attached {CMS_USER} l_user_api.user_by_email (l_email.value) as l_user then + if attached {WSF_STRING} req.form_parameter ("email") as p_email then + if p_email.value.is_valid_as_string_8 then + l_email := p_email.value.to_string_8 + if attached {CMS_USER} l_user_api.user_by_email (l_email) as l_user then -- 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) - l_url := req.absolute_script_url ("/account/reset-password?token=" + l_token) + l_token := new_token + l_user_api.new_password (l_token, l_user.id) + 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_url) + 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, l_user, l_url, req.absolute_script_url ("")) + else + r.set_value ("The email does not exist !", "error_email") + r.set_value (p_email.value, "email") + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + end else - r.set_value ("The email does not exist !", "error_email") - r.set_value (l_email.value, "email") + r.set_value ("The email is not valid!", "error_email") + r.set_value (p_email.value, "email") r.set_status_code ({HTTP_CONSTANTS}.bad_request) end elseif attached {WSF_STRING} req.form_parameter ("username") as l_username then - if attached {CMS_USER} l_user_api.user_by_name (l_username) as l_user and then - attached l_user.email as l_email + if + attached {CMS_USER} l_user_api.user_by_name (l_username) as l_user and then + attached l_user.email as l_user_email then - -- User exist create a new token and send a new email. + -- 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) l_url := req.absolute_script_url ("/account/reset-password?token=" + l_token) - -- Send Email + -- 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, l_url) + es.send_contact_password_email (l_user_email, l_user, l_url, req.absolute_script_url ("")) else r.set_value ("The username does not exist !", "error_username") r.set_value (l_username.value, "username") @@ -360,7 +486,6 @@ feature -- Handler r.execute end - handle_reset_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE @@ -368,24 +493,18 @@ feature -- Handler do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) l_user_api := api.user_api - if attached {WSF_STRING} req.query_parameter ("token") as l_token then + if attached {WSF_STRING} req.query_parameter ("token") as l_token then r.set_value (l_token.value, "token") - if l_user_api.user_by_password_token (l_token.value) = Void then - r.set_value ("The token " + l_token.value + " is not valid, " + r.link ("click here" , "account/new-password", Void) + " to generate a new token.", "error_token") + if l_user_api.user_by_password_token (l_token.value) = Void then + r.set_value ("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 - if req.is_post_request_method then - - if - attached {WSF_STRING} req.form_parameter ("token") as l_token and then - attached {WSF_STRING} req.form_parameter ("password") as l_password and then - attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password - then - -- Does the passwords match? + if attached {WSF_STRING} req.form_parameter ("token") as l_token and then attached {WSF_STRING} req.form_parameter ("password") as l_password and then attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password then + -- Does the passwords match? if l_password.value.same_string (l_confirm_password.value) then - -- is the token valid? + -- is the token valid? if attached {CMS_USER} l_user_api.user_by_password_token (l_token.value) as l_user then l_user.set_password (l_password.value) l_user_api.update_user (l_user) @@ -408,22 +527,17 @@ feature -- Handler do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) l_user_api := api.user_api - if req.is_post_request_method then - if attached r.user as l_user then + if attached r.user as l_user then r.set_value (api.user_api.user_roles (l_user), "roles") - if - attached {WSF_STRING} req.form_parameter ("password") as l_password and then - attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password and then - l_password.value.same_string (l_confirm_password.value) - then - -- Does the passwords match? + if attached {WSF_STRING} req.form_parameter ("password") as l_password and then attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password and then l_password.value.same_string (l_confirm_password.value) then + -- Does the passwords match? l_user.set_password (l_password.value) l_user_api.update_user (l_user) r.set_redirection (req.absolute_script_url ("/account/post-change-password")) else if attached template_block ("account_info", r) as l_tpl_block then --- r.set_value (l_user, "user") + -- r.set_value (l_user, "user") r.set_value ("Passwords Don't Match", "error_password") r.set_status_code ({HTTP_CONSTANTS}.bad_request) r.add_block (l_tpl_block, "content") @@ -445,11 +559,102 @@ feature -- Handler r.execute end + handle_admin_pending_registrations (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API) + local + l_response: CMS_RESPONSE + s: STRING + u: CMS_TEMP_USER + l_page_helper: CMS_PAGINATION_GENERATOR + s_pager: STRING + l_count: INTEGER + l_user_api: CMS_USER_API + do + -- At the moment the template are hardcoded, but we can + -- get them from the configuration file and load them into + -- the setup class. + + create {FORBIDDEN_ERROR_CMS_RESPONSE} l_response.make (req, res, api) + if + l_response.has_permission ("admin registration") + then + l_user_api := api.user_api + + l_count := l_user_api.temp_users_count + + create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api) + + create s.make_empty + if l_count > 1 then + l_response.set_title ("Listing " + l_count.out + " Pending Registrations") + else + l_response.set_title ("Listing " + l_count.out + " Pending Registration") + end + + create s_pager.make_empty + create l_page_helper.make ("admin/pending-registrations/?page={page}&size={size}", l_user_api.temp_users_count.as_natural_64, 25) -- FIXME: Make this default page size a global CMS settings + l_page_helper.get_setting_from_request (req) + if l_page_helper.has_upper_limit and then l_page_helper.pages_count > 1 then + l_page_helper.append_to_html (l_response, s_pager) + if l_page_helper.page_size > 25 then + s.append (s_pager) + end + end + + if attached l_user_api.temp_recent_users (create {CMS_DATA_QUERY_PARAMETERS}.make (l_page_helper.current_page_offset, l_page_helper.page_size)) as lst then + s.append ("

    %N") + across + lst as ic + loop + u := ic.item + s.append ("
  • ") + s.append ("User:" + html_encoded (u.name)) + s.append ("
      ") + if attached u.personal_information as l_information then + s.append ("
    • ") + s.append (html_encoded (l_information)) + s.append ("
    • %N") + end + if attached u.email as l_email then + s.append ("
    • ") + s.append (l_email) + s.append ("
    • %N") + end + if attached l_user_api.token_by_temp_user_id (u.id) as l_token then + s.append ("
    • ") + s.append ("") + s.append (html_encoded ("Activate")) + s.append ("") + s.append ("
    • %N") + s.append ("
    • ") + s.append ("") + s.append (html_encoded ("Reject")) + s.append ("") + s.append ("
    • %N") + end + s.append ("
    %N") + s.append ("
  • %N") + end + s.append ("
%N") + end + -- Again the pager at the bottom, if needed + s.append (s_pager) + + l_response.set_main_content (s) + l_response.execute + else + l_response.execute + end + end + block_list: ITERABLE [like {CMS_BLOCK}.name] local l_string: STRING do - Result := <<"register", "reactivate", "new_password", "reset_password">> + Result := <<"register", "reactivate", "new_password", "reset_password", "registration">> debug ("roc") create l_string.make_empty across @@ -458,32 +663,22 @@ feature -- Handler l_string.append (ic.item) l_string.append_character (' ') end - write_debug_log (generator + ".block_list:" + l_string ) + write_debug_log (generator + ".block_list:" + l_string) end end get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) do - if - a_block_id.is_case_insensitive_equal_general ("register") and then - a_response.location.starts_with ("account/roc-register") - then + if a_block_id.is_case_insensitive_equal_general ("register") and then 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.location.starts_with ("account/reactivate") - then + elseif a_block_id.is_case_insensitive_equal_general ("reactivate") and then 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.location.starts_with ("account/new-password") - then + elseif a_block_id.is_case_insensitive_equal_general ("new_password") and then 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.location.starts_with ("account/reset-password") - then + elseif a_block_id.is_case_insensitive_equal_general ("reset_password") and then a_response.location.starts_with ("account/reset-password") then get_block_view_reset_password (a_block_id, a_response) + elseif a_block_id.is_case_insensitive_equal_general ("registration") and then a_response.location.starts_with ("admin/pending-registrations") then + get_block_view_registration (a_block_id, a_response) end end @@ -499,10 +694,13 @@ feature {NONE} -- Token Generation create l_security l_token := l_security.token create l_encode - from until l_token.same_string (l_encode.encoded_string (l_token)) loop - -- Loop ensure that we have a security token that does not contain characters that need encoding. - -- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token - -- but the user will need to use an unencoded token if activation has to be done manually. + from + until + l_token.same_string (l_encode.encoded_string (l_token)) + loop + -- Loop ensure that we have a security token that does not contain characters that need encoding. + -- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token + -- but the user will need to use an unencoded token if activation has to be done manually. l_token := l_security.token end Result := l_token @@ -517,7 +715,6 @@ feature {NONE} -- Helpers do create p.make_from_string ("templates") p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl") - p := a_response.api.module_theme_resource_location (Current, p) if p /= Void then if attached p.entry as e then @@ -530,32 +727,14 @@ feature {NONE} -- Helpers feature {NONE} -- Block views --- get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) --- local ----- vals: CMS_VALUE_TABLE --- do --- if attached template_block (a_block_id, a_response) as l_tpl_block then ----- create vals.make (1) ----- -- add the variable to the block ----- value_table_alter (vals, a_response) ----- across ----- vals as ic ----- loop ----- l_tpl_block.set_value (ic.item, ic.key) ----- end --- a_response.put_required_block (l_tpl_block, "content") --- else --- debug ("cms") --- a_response.add_warning_message ("Error with block [" + a_block_id + "]") --- end --- end --- end - get_block_view_register (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) do if a_response.has_permission ("account register") then if a_response.request.is_get_request_method then if attached template_block (a_block_id, a_response) as l_tpl_block then + if attached recaptcha_site_key (a_response.api) as l_recaptcha_site_key then + l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key") + end a_response.add_block (l_tpl_block, "content") else debug ("cms") @@ -565,10 +744,13 @@ feature {NONE} -- Block views elseif a_response.request.is_post_request_method then if a_response.values.has ("error_name") or else a_response.values.has ("error_email") then if attached template_block (a_block_id, a_response) as l_tpl_block then - -- l_tpl_block.set_value (a_response.values.item ("error_name"), "error_name") - -- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") - -- l_tpl_block.set_value (a_response.values.item ("email"), "email") - -- l_tpl_block.set_value (a_response.values.item ("name"), "name") + -- l_tpl_block.set_value (a_response.values.item ("error_name"), "error_name") + -- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") + -- l_tpl_block.set_value (a_response.values.item ("email"), "email") + -- l_tpl_block.set_value (a_response.values.item ("name"), "name") + if attached recaptcha_site_key (a_response.api) as l_recaptcha_site_key then + l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key") + end a_response.add_block (l_tpl_block, "content") else debug ("cms") @@ -601,9 +783,9 @@ feature {NONE} -- Block views elseif a_response.request.is_post_request_method then if a_response.values.has ("error_email") or else a_response.values.has ("is_active") then if attached template_block (a_block_id, a_response) as l_tpl_block then --- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") --- l_tpl_block.set_value (a_response.values.item ("email"), "email") --- l_tpl_block.set_value (a_response.values.item ("is_active"), "is_active") + -- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") + -- l_tpl_block.set_value (a_response.values.item ("email"), "email") + -- l_tpl_block.set_value (a_response.values.item ("is_active"), "is_active") a_response.add_block (l_tpl_block, "content") else debug ("cms") @@ -633,12 +815,12 @@ feature {NONE} -- Block views end end elseif a_response.request.is_post_request_method then - if a_response.values.has ("error_email") or else a_response.values.has ("error_username") then + if a_response.values.has ("error_email") or else a_response.values.has ("error_username") then if attached template_block (a_block_id, a_response) as l_tpl_block then --- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") --- l_tpl_block.set_value (a_response.values.item ("email"), "email") --- l_tpl_block.set_value (a_response.values.item ("error_username"), "error_username") --- l_tpl_block.set_value (a_response.values.item ("username"), "username") + -- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") + -- l_tpl_block.set_value (a_response.values.item ("email"), "email") + -- l_tpl_block.set_value (a_response.values.item ("error_username"), "error_username") + -- l_tpl_block.set_value (a_response.values.item ("username"), "username") a_response.add_block (l_tpl_block, "content") else debug ("cms") @@ -661,8 +843,8 @@ feature {NONE} -- Block views do if a_response.request.is_get_request_method then if attached template_block (a_block_id, a_response) as l_tpl_block then --- l_tpl_block.set_value (a_response.values.item ("token"), "token") --- l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") + -- l_tpl_block.set_value (a_response.values.item ("token"), "token") + -- l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") a_response.add_block (l_tpl_block, "content") else debug ("cms") @@ -670,11 +852,11 @@ feature {NONE} -- Block views end end elseif a_response.request.is_post_request_method then - if a_response.values.has ("error_token") or else a_response.values.has ("error_password") then + if a_response.values.has ("error_token") or else a_response.values.has ("error_password") then if attached template_block (a_block_id, a_response) as l_tpl_block then --- l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") --- l_tpl_block.set_value (a_response.values.item ("error_password"), "error_password") --- l_tpl_block.set_value (a_response.values.item ("token"), "token") + -- l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") + -- l_tpl_block.set_value (a_response.values.item ("error_password"), "error_password") + -- l_tpl_block.set_value (a_response.values.item ("token"), "token") a_response.add_block (l_tpl_block, "content") else debug ("cms") @@ -693,14 +875,75 @@ feature {NONE} -- Block views end end + get_block_view_registration (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + do + end + +feature -- Recaptcha + + recaptcha_secret_key (api: CMS_API): detachable READABLE_STRING_8 + -- Get recaptcha security key. + local + utf: UTF_CONVERTER + do + if attached api.module_configuration (Current, Void) as cfg then + if attached cfg.text_item ("recaptcha.secret_key") as l_recaptcha_key and then not l_recaptcha_key.is_empty then + Result := utf.utf_32_string_to_utf_8_string_8 (l_recaptcha_key) + end + end + end + + recaptcha_site_key (api: CMS_API): detachable READABLE_STRING_8 + -- Get recaptcha security key. + local + utf: UTF_CONVERTER + do + if attached api.module_configuration (Current, Void) as cfg then + if attached cfg.text_item ("recaptcha.site_key") as l_recaptcha_key and then not l_recaptcha_key.is_empty then + Result := utf.utf_32_string_to_utf_8_string_8 (l_recaptcha_key) + end + end + end + +feature -- Response Alter + + response_alter (a_response: CMS_RESPONSE) + do + a_response.add_javascript_url ("https://www.google.com/recaptcha/api.js") + a_response.add_style (a_response.url ("/module/" + name + "/files/css/auth.css", Void), Void) + end +feature {NONE} -- Implementation + + is_captcha_verified (a_secret, a_response: READABLE_STRING_8): BOOLEAN + local + api: RECAPTCHA_API + l_errors: STRING + do + write_debug_log (generator + ".is_captcha_verified with response: [" + a_response + "]") + create api.make (a_secret, a_response) + Result := api.verify + if not Result and then attached api.errors as l_api_errors then + create l_errors.make_empty + l_errors.append_character ('%N') + across + l_api_errors as ic + loop + l_errors.append (ic.item) + l_errors.append_character ('%N') + end + write_error_log (generator + ".is_captcha_verified api_errors [" + l_errors + "]") + 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)" source: "[ - Eiffel Software - 5949 Hollister Ave., Goleta, CA 93117 USA - Telephone 805-685-1006, Fax 805-685-6869 - Website http://www.eiffel.com - Customer support http://support.eiffel.com - ]" + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" + end diff --git a/modules/auth/site/config/auth.json b/modules/auth/site/config/auth.json new file mode 100644 index 0000000..728b433 --- /dev/null +++ b/modules/auth/site/config/auth.json @@ -0,0 +1,7 @@ +{ + "subject": "Thank you for contacting us", + "recaptcha": { + "site_key":"6Lex9RMTAAAAAKleC4x6TaRlFcpLbEWgH_U7MSiD", + "secret_key":"6Lex9RMTAAAAAAkBczvX5DUiyg_xoM_EthVVgRRx" + } +} diff --git a/modules/auth/site/files/css/auth.css b/modules/auth/site/files/css/auth.css new file mode 100644 index 0000000..d57de12 --- /dev/null +++ b/modules/auth/site/files/css/auth.css @@ -0,0 +1,28 @@ +ul.cms-temp-users { + list-style-type: none; + padding: 3px 3px 3px 3px; + border: solid 1px #ccc; +} +ul.cms-temp-users li { + border-top: dotted 1px #ccc; +} +ul.cms-temp-users li:first-child { + border-top: none; +} +ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details { + list-style-type: none; + padding: 3px 3px 3px 3px; + border: solid 1px #ccc; +} +ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li { + border-top: dotted 1px #ccc; +} +ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li:first-child { + border-top: none; +} +ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li.cms_temp_user_detail_information::before { + content: "[personal information] "; +} +ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li.cms_temp_user_detail_email::before { + content: "[email] "; +} diff --git a/modules/auth/site/files/scss/auth.scss b/modules/auth/site/files/scss/auth.scss new file mode 100644 index 0000000..38fe7b5 --- /dev/null +++ b/modules/auth/site/files/scss/auth.scss @@ -0,0 +1,37 @@ +ul.cms-temp-users { + + list-style-type: none; + padding: 3px 3px 3px 3px; + border: solid 1px #ccc; + + li{ + border-top: dotted 1px #ccc; + &:first-child { + border-top: none; + } + } + + li.cms_temp_user { + + ul.cms_temp_user_details { + list-style-type: none; + padding: 3px 3px 3px 3px; + border: solid 1px #ccc; + + li{ + border-top: dotted 1px #ccc; + &:first-child { + border-top: none; + } + } + li.cms_temp_user_detail_information::before{ + content: "[personal information] " + } + li.cms_temp_user_detail_email::before{ + content: "[email] " + } + } + } +} + + diff --git a/modules/auth/site/mail_templates/account_activation.html b/modules/auth/site/mail_templates/account_activation.html index 0ab4c4f..5070ce8 100644 --- a/modules/auth/site/mail_templates/account_activation.html +++ b/modules/auth/site/mail_templates/account_activation.html @@ -4,15 +4,10 @@ Activation - + - -

Thank you for registering at ROC CMS

- -

To complete your registration, please click on this link to activate your account:

- -

$link

-

Thank you for joining us.

+

"$user ($email)", thank you for applying to $sitename.

+

We will review your application and send you a resolution.

diff --git a/modules/auth/site/mail_templates/account_activation_confirmation.html b/modules/auth/site/mail_templates/account_activation_confirmation.html new file mode 100644 index 0000000..4dfacf5 --- /dev/null +++ b/modules/auth/site/mail_templates/account_activation_confirmation.html @@ -0,0 +1,13 @@ + + + + + Activation Confirmation + + + + +

Your account "$user ($email)" is confirmed at $sitename.

+

Thank you for joining us.

+ + diff --git a/modules/auth/site/mail_templates/account_new_password.html b/modules/auth/site/mail_templates/account_new_password.html index ad2792d..039268e 100644 --- a/modules/auth/site/mail_templates/account_new_password.html +++ b/modules/auth/site/mail_templates/account_new_password.html @@ -4,14 +4,12 @@ New Password - + - -

You have required a new password at ROC CMS

- -

To complete your request, please click on this link to genereate a new password:

- -

$link

+

You have requested a new password at $sitename.

+

To complete your request, please click on the following link to generate a new password: +

+

diff --git a/modules/auth/site/mail_templates/account_re_activation.html b/modules/auth/site/mail_templates/account_re_activation.html index 3590804..c2fbaec 100644 --- a/modules/auth/site/mail_templates/account_re_activation.html +++ b/modules/auth/site/mail_templates/account_re_activation.html @@ -4,15 +4,14 @@ New Activation - + - -

You have request a new activation token at ROC CMS

+

You have requested a new activation token at $sitename.

-

To complete your registration, please click on this link to activate your account:

- -

$link

+

To complete your registration, please click on the following link to re-activate your account: +

+

Thank you for joining us.

diff --git a/modules/auth/site/mail_templates/account_rejected.html b/modules/auth/site/mail_templates/account_rejected.html new file mode 100644 index 0000000..96cf8bd --- /dev/null +++ b/modules/auth/site/mail_templates/account_rejected.html @@ -0,0 +1,12 @@ + + + + + Application Rejected + + + + +

Your account application is rejected, it was not respecting the requirements from $sitename.

+ + diff --git a/modules/auth/site/mail_templates/account_welcome.html b/modules/auth/site/mail_templates/account_welcome.html index facecee..c4e89d1 100644 --- a/modules/auth/site/mail_templates/account_welcome.html +++ b/modules/auth/site/mail_templates/account_welcome.html @@ -4,10 +4,16 @@ Welcome - + -

Welcome toROC CMS

+

Welcome to $sitename.

+

Your account information: +

    +
  • Email address: "$email" .
  • +
  • User name: "$user" .
  • +
+

Thank you for joining us.

diff --git a/modules/auth/site/mail_templates/admin_account_evaluation.html b/modules/auth/site/mail_templates/admin_account_evaluation.html new file mode 100644 index 0000000..dcecbac --- /dev/null +++ b/modules/auth/site/mail_templates/admin_account_evaluation.html @@ -0,0 +1,26 @@ + + + + + Account Evaluation + + + + + +

Account Evaluation

+

The user $user ($email) wants to register to the site $sitename

+ +

User application:

+

$application

+
+ +

To complete the registration, please click on the following link to activate the user account:

+ +

$activation_url

+ +

To reject the registration, please click on the following link

+ +

$rejection_url

+ + diff --git a/modules/auth/site/templates/block_post_reactivate.tpl b/modules/auth/site/templates/block_post_reactivate.tpl index 09e7206..c76cbb5 100644 --- a/modules/auth/site/templates/block_post_reactivate.tpl +++ b/modules/auth/site/templates/block_post_reactivate.tpl @@ -1,3 +1,3 @@
-

We have send you a new activation code, check your email to activate your account.

+

Thanks for your application, we will review it to activate your account.

diff --git a/modules/auth/site/templates/block_post_register.tpl b/modules/auth/site/templates/block_post_register.tpl index d59f75a..c76cbb5 100644 --- a/modules/auth/site/templates/block_post_register.tpl +++ b/modules/auth/site/templates/block_post_register.tpl @@ -1,3 +1,3 @@
-

Thanks for register, check your email to activate your account.

+

Thanks for your application, we will review it to activate your account.

diff --git a/modules/auth/site/templates/block_register.tpl b/modules/auth/site/templates/block_register.tpl index 9e7c478..39104ef 100644 --- a/modules/auth/site/templates/block_register.tpl +++ b/modules/auth/site/templates/block_register.tpl @@ -1,7 +1,7 @@
- Register Form + Registration
@@ -20,8 +20,19 @@ {$error_email/}
{/if}
- - +
+ + + {if isset="$error_application"} + {$error_application/}
+ {/if} +
+ {unless isempty="$recaptcha_site_key"} +
+
+ {/unless}
diff --git a/modules/blog/handler/blog_handler.e b/modules/blog/handler/blog_handler.e index 1e4b28e..98522ea 100644 --- a/modules/blog/handler/blog_handler.e +++ b/modules/blog/handler/blog_handler.e @@ -206,7 +206,7 @@ feature -- HTML Output do if attached n.author as l_author then a_output.append ("by ") - a_output.append ("" + l_author.name + "") + a_output.append ("" + html_encoded (l_author.name) + "") end end diff --git a/modules/oauth20/cms_oauth_20_module.e b/modules/oauth20/cms_oauth_20_module.e index 982d3e3..230b451 100644 --- a/modules/oauth20/cms_oauth_20_module.e +++ b/modules/oauth20/cms_oauth_20_module.e @@ -493,7 +493,7 @@ feature -- OAuth2 Login with Provider -- Send Email create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) write_debug_log (generator + ".handle_callback_oauth: send_contact_welcome_email") - es.send_contact_welcome_email (l_email, "") + es.send_contact_welcome_email (l_email, l_user, req.absolute_script_url ("")) end end r.set_redirection (r.front_page_url) diff --git a/modules/openid/cms_openid_module.e b/modules/openid/cms_openid_module.e index 959e606..e57b13a 100644 --- a/modules/openid/cms_openid_module.e +++ b/modules/openid/cms_openid_module.e @@ -443,7 +443,7 @@ feature -- Openid Login -- Send Email create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) write_debug_log (generator + ".handle_callback_openid: send_contact_welcome_email") - es.send_contact_welcome_email (l_email, "") + es.send_contact_welcome_email (l_email, l_user, req.absolute_script_url ("")) end end r.set_redirection (r.front_page_url) diff --git a/src/configuration/cms_setup.e b/src/configuration/cms_setup.e index baf3a97..fac2593 100644 --- a/src/configuration/cms_setup.e +++ b/src/configuration/cms_setup.e @@ -16,7 +16,7 @@ feature {NONE} -- Initialization initialize local l_url: like site_url - l_email: detachable READABLE_STRING_8 + s, l_email: detachable READABLE_STRING_8 do site_location := environment.path @@ -51,27 +51,32 @@ feature {NONE} -- Initialization site_email := l_email -- Email address for current web site - --| Also known site_notification_email := string_8_item_or_default ("notification.email", site_email) + -- Email subject tuning. + s := string_8_item ("mailer.subject_prefix") + if s /= Void and then not s.ends_with_general (" ") then + s := s + " " + end + site_email_subject_prefix := s -- Location for public files - if attached text_item ("files-dir") as s then - create files_location.make_from_string (s) + if attached text_item ("files-dir") as l_files_dir then + create files_location.make_from_string (l_files_dir) else files_location := site_location.extended ("files") end -- Location for modules folders. - if attached text_item ("modules-dir") as s then - create modules_location.make_from_string (s) + if attached text_item ("modules-dir") as l_modules_dir then + create modules_location.make_from_string (l_modules_dir) else modules_location := environment.modules_path end -- Location for themes folders. - if attached text_item ("themes-dir") as s then - create themes_location.make_from_string (s) + if attached text_item ("themes-dir") as l_themes_dir then + create themes_location.make_from_string (l_themes_dir) else themes_location := environment.themes_path end @@ -196,6 +201,14 @@ feature -- Access: Site site_name: READABLE_STRING_32 -- Name of the site. + utf_8_site_name: READABLE_STRING_8 + -- `site_name' encoded with UTF-8. + local + utf: UTF_CONVERTER + do + Result := utf.utf_32_string_to_utf_8_string_8 (site_name) + end + site_email: READABLE_STRING_8 -- Website email address. -- Used as "From:" address when the site is sending emails @@ -204,6 +217,9 @@ feature -- Access: Site site_notification_email: READABLE_STRING_8 -- Email address receiving internal notification. + site_email_subject_prefix: detachable READABLE_STRING_8 + -- Optional prefix for any email sent by Current site. + site_url: detachable READABLE_STRING_8 -- Optional url of current CMS site. diff --git a/src/persistence/user/cms_user_storage_i.e b/src/persistence/user/cms_user_storage_i.e index 2692e1b..0e28a43 100644 --- a/src/persistence/user/cms_user_storage_i.e +++ b/src/persistence/user/cms_user_storage_i.e @@ -216,11 +216,6 @@ feature -- Change: User activation deferred end - remove_activation (a_token: READABLE_STRING_32) - -- . - deferred - end - feature -- Change: User password recovery save_password (a_token: READABLE_STRING_32; a_id: INTEGER_64) @@ -233,7 +228,87 @@ feature -- Change: User password recovery deferred end +feature -- Access: Temp Users + + temp_users_count: INTEGER + -- Number of pending users + --! to be accepted or rejected + deferred + end + + temp_user_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER + -- Retrieve a temporal user by id `a_uid' for the consumer `a_consumer', if aby. + deferred + end + + temp_user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + -- User with name `a_name', if any. + require + a_name /= Void and then not a_name.is_empty + deferred + ensure + same_name: Result /= Void implies a_name ~ Result.name + password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void) + end + + temp_user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + -- User with name `a_email', if any. + deferred + ensure + same_email: Result /= Void implies a_email ~ Result.email + password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void) + end + + + temp_user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- User with activation token `a_token', if any. + deferred + ensure + password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void) + end + + temp_recent_users (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_TEMP_USER] + -- List of recent `a_count' temporal users with an offset of `lower'. + deferred + end + + + token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING + -- Retrieve activation token for user identified with id `a_id', if any. + deferred + end + + +feature -- New Temp User + + new_user_from_temp_user (a_user: CMS_TEMP_USER) + -- new user from temporal user `a_user' + require + no_id: not a_user.has_id + deferred + end + + remove_activation (a_token: READABLE_STRING_32) + -- Remove activation by token `a_token'. + deferred + end + + new_temp_user (a_user: CMS_TEMP_USER) + -- New temp user `a_user'. + require + no_id: not a_user.has_id + deferred + end + + delete_temp_user (a_user: CMS_TEMP_USER) + -- Delete user `a_user'. + require + has_id: a_user.has_id + deferred + end + + note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/persistence/user/cms_user_storage_null.e b/src/persistence/user/cms_user_storage_null.e index c899696..2399479 100644 --- a/src/persistence/user/cms_user_storage_null.e +++ b/src/persistence/user/cms_user_storage_null.e @@ -131,10 +131,6 @@ feature -- Change: User activation do end - remove_activation (a_token: READABLE_STRING_32) - -- . - do - end feature -- Change: User password recovery @@ -148,7 +144,67 @@ feature -- Change: User password recovery do end +feature -- Access: Users + + temp_users_count: INTEGER + -- + do + end + + temp_user_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER + -- + do + end + + temp_user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + -- + do + end + + temp_user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + -- + do + end + + temp_user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- + do + end + + temp_recent_users (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_TEMP_USER] + -- List of recent `a_count' temporal users with an offset of `lower'. + do + create {ARRAYED_LIST[CMS_TEMP_USER]} Result.make (0) + end + + token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING + -- + do + end + +feature -- Temp Users + + new_user_from_temp_user (a_user: CMS_TEMP_USER) + -- + do + end + + + remove_activation (a_token: READABLE_STRING_32) + -- . + do + end + + new_temp_user (a_user: CMS_TEMP_USER) + -- + do + end + + delete_temp_user (a_user: CMS_TEMP_USER) + -- + do + end note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/persistence/user/cms_user_storage_sql_i.e b/src/persistence/user/cms_user_storage_sql_i.e index 640d6ef..3cbc6d9 100644 --- a/src/persistence/user/cms_user_storage_sql_i.e +++ b/src/persistence/user/cms_user_storage_sql_i.e @@ -196,6 +196,7 @@ feature -- Access: user end sql_finalize end + feature -- Change: user new_user (a_user: CMS_USER) @@ -755,21 +756,6 @@ feature -- Change: User activation sql_finalize end - remove_activation (a_token: READABLE_STRING_32) - -- . - local - l_parameters: STRING_TABLE [detachable ANY] - do - error_handler.reset - sql_begin_transaction - write_information_log (generator + ".remove_activation") - create l_parameters.make (1) - l_parameters.put (a_token, "token") - sql_modify (sql_remove_activation, l_parameters) - sql_commit_transaction - sql_finalize - end - feature -- Change: User password recovery save_password (a_token: READABLE_STRING_32; a_id: INTEGER_64) @@ -866,19 +852,6 @@ feature {NONE} -- Implementation: User end end - last_inserted_user_id: INTEGER_64 - -- Last insert user id. - do - error_handler.reset - write_information_log (generator + ".last_inserted_user_id") - sql_query (Sql_last_insert_user_id, Void) - if not sql_after then - Result := sql_read_integer_64 (1) - sql_forth - check one_row: sql_after end - end - sql_finalize - end feature {NONE} -- Implementation: User role @@ -909,8 +882,6 @@ feature {NONE} -- Sql Queries: USER Select_users_count: STRING = "SELECT count(*) FROM users;" -- Number of users. - Sql_last_insert_user_id: STRING = "SELECT MAX(uid) FROM users;" - Select_users: STRING = "SELECT * FROM users;" -- List of users. @@ -920,7 +891,7 @@ feature {NONE} -- Sql Queries: USER Select_user_by_name: STRING = "SELECT * FROM users WHERE name =:name;" -- Retrieve user by name if exists. - Sql_select_recent_users: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM users ORDER BY uid DESC, created DESC LIMIT :rows OFFSET :offset ;" + Sql_select_recent_users: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM users ORDER BY uid DESC, created DESC LIMIT :rows OFFSET :offset;" -- Retrieve recent users Select_user_by_email: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM users WHERE email =:email;" @@ -941,6 +912,8 @@ feature {NONE} -- Sql Queries: USER ROLE sql_last_insert_user_role_id: STRING = "SELECT MAX(rid) FROM roles;" + sql_last_insert_user_id: STRING = "SELECT MAX(uid) FROM users;" + select_user_roles: STRING = "SELECT rid, name FROM roles;" -- List of user roles. @@ -1009,7 +982,368 @@ feature {NONE} -- User Password Recovery Select_user_by_password_token: STRING = "SELECT u.* FROM users as u JOIN users_password_recovery as ua ON ua.uid = u.uid and ua.token = :token;" -- Retrieve user by password token if exist. + + +feature -- Acess: Temp users + + temp_users_count: INTEGER + -- Number of items users. + do + error_handler.reset + write_information_log (generator + ".temp_users_count") + + sql_query (select_temp_users_count, Void) + if not has_error and then not sql_after then + Result := sql_read_integer_64 (1).to_integer_32 + sql_forth + check one_row: sql_after end + end + sql_finalize + end + + + temp_user_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER + -- + local + l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING + do + error_handler.reset + write_information_log (generator + ".temp_user_by_id") + create l_parameters.make (1) + l_parameters.put (a_uid, "uid") + create l_string.make_from_string (select_user_auth_temp_by_id) + sql_query (l_string, l_parameters) + if not has_error and not sql_after then + Result := fetch_temp_user + sql_forth + if not sql_after then + check no_more_than_one: False end + Result := Void + end + end + sql_finalize + end + + temp_user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + -- User for the given name `a_name', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".temp_user_by_name") + create l_parameters.make (1) + l_parameters.put (a_name, "name") + sql_query (select_temp_user_by_name, l_parameters) + if not sql_after then + Result := fetch_temp_user + sql_forth + check one_row: sql_after end + end + sql_finalize + end + + temp_user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + -- User for the given email `a_email', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".temp_user_by_name") + create l_parameters.make (1) + l_parameters.put (a_email, "email") + sql_query (select_temp_user_by_email, l_parameters) + if not sql_after then + Result := fetch_temp_user + sql_forth + check one_row: sql_after end + end + sql_finalize + end + + temp_user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- User for the given activation token `a_token', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".temp_user_by_activation_token") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_query (select_temp_user_by_activation_token, l_parameters) + if not sql_after then + Result := fetch_temp_user + sql_forth + check one_row: sql_after end + end + sql_finalize + end + + temp_recent_users (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_TEMP_USER] + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + create {ARRAYED_LIST [CMS_TEMP_USER]} Result.make (0) + + error_handler.reset + write_information_log (generator + ".temp_recent_users") + + from + create l_parameters.make (2) + l_parameters.put (a_count, "rows") + l_parameters.put (a_lower, "offset") + sql_query (sql_select_temp_recent_users, l_parameters) + sql_start + until + sql_after + loop + if attached fetch_temp_user as l_user then + Result.force (l_user) + end + sql_forth + end + sql_finalize + end + + token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING + -- Number of items users. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".token_by_temp_user_id") + create l_parameters.make (1) + l_parameters.put (a_id, "uid") + + + sql_query (select_token_activation_by_user_id, l_parameters) + if not has_error and then not sql_after then + Result := sql_read_string (1) + sql_forth + check one_row: sql_after end + end + sql_finalize + end + +feature {NONE} -- Implementation: User + + fetch_temp_user: detachable CMS_TEMP_USER + local + l_id: INTEGER_64 + l_name: detachable READABLE_STRING_32 + do + if attached sql_read_integer_64 (1) as i then + l_id := i + end + if attached sql_read_string_32 (2) as s and then not s.is_whitespace then + l_name := s + end + + if l_name /= Void then + create Result.make (l_name) + if l_id > 0 then + Result.set_id (l_id) + end + elseif l_id > 0 then + create Result.make_with_id (l_id) + end + + if Result /= Void then + if attached sql_read_string (3) as l_password then + Result.set_hashed_password (l_password) + end + if attached sql_read_string (4) as l_salt then + Result.set_salt (l_salt) + end + if attached sql_read_string (5) as l_email then + Result.set_email (l_email) + end + if attached sql_read_string (6) as l_application then + Result.set_personal_information (l_application) + end + else + check expected_valid_user: False end + end + end + + +feature -- New Temp User + + new_user_from_temp_user (a_user: CMS_TEMP_USER) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + if + attached a_user.hashed_password as l_password_hash and then + attached a_user.email as l_email and then + attached a_user.salt as l_password_salt + then + -- FIXME: store the personal_information in profile! + sql_begin_transaction + + write_information_log (generator + ".new_user_from_temp_user") + create l_parameters.make (4) + l_parameters.put (a_user.name, "name") + l_parameters.put (l_password_hash, "password") + l_parameters.put (l_password_salt, "salt") + l_parameters.put (l_email, "email") + l_parameters.put (create {DATE_TIME}.make_now_utc, "created") + l_parameters.put (a_user.status, "status") + + sql_insert (sql_insert_user, l_parameters) + if not error_handler.has_error then + a_user.set_id (last_inserted_user_id) + end + if not error_handler.has_error then + sql_commit_transaction + else + sql_rollback_transaction + end + sql_finalize + else + -- set error + error_handler.add_custom_error (-1, "bad request" , "Missing password or email") + end + end + + new_temp_user (a_user: CMS_TEMP_USER) + -- Add a new temp_user `a_user'. + local + l_parameters: STRING_TABLE [detachable ANY] + l_password_salt, l_password_hash: STRING + l_security: SECURITY_PROVIDER + do + error_handler.reset + if + attached a_user.password as l_password and then + attached a_user.email as l_email and then + attached a_user.personal_information as l_personal_information + then + + create l_security + l_password_salt := l_security.salt + l_password_hash := l_security.password_hash (l_password, l_password_salt) + + write_information_log (generator + ".new_temp_user") + create l_parameters.make (4) + l_parameters.put (a_user.name, "name") + l_parameters.put (l_password_hash, "password") + l_parameters.put (l_password_salt, "salt") + l_parameters.put (l_email, "email") + l_parameters.put (l_personal_information, "application") + + sql_begin_transaction + sql_insert (sql_insert_temp_user, l_parameters) + if not error_handler.has_error then + a_user.set_id (last_inserted_temp_user_id) + sql_commit_transaction + else + sql_rollback_transaction + end + sql_finalize + else + -- set error + error_handler.add_custom_error (-1, "bad request" , "Missing password or email or personal information") + end + end + +feature -- Remove Activation + + remove_activation (a_token: READABLE_STRING_32) + -- . + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + sql_begin_transaction + write_information_log (generator + ".remove_activation") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_modify (sql_remove_activation, l_parameters) + sql_commit_transaction + sql_finalize + end + + delete_temp_user (a_user: CMS_TEMP_USER) + -- Delete user `a_user'. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + sql_begin_transaction + write_information_log (generator + ".delete_temp_user") + create l_parameters.make (1) + l_parameters.put (a_user.id, "uid") + sql_modify (sql_delete_temp_user, l_parameters) + sql_commit_transaction + sql_finalize + end + +feature {NONE} -- Implementation + + last_inserted_temp_user_id: INTEGER_64 + -- Last insert user id. + do + error_handler.reset + write_information_log (generator + ".last_inserted_temp_user_id") + sql_query (sql_last_insert_temp_user_id, Void) + if not sql_after then + Result := sql_read_integer_64 (1) + sql_forth + check one_row: sql_after end + end + sql_finalize + end + + last_inserted_user_id: INTEGER_64 + -- Last insert user id. + do + error_handler.reset + write_information_log (generator + ".last_inserted_user_id") + sql_query (sql_last_insert_user_id, Void) + if not sql_after then + Result := sql_read_integer_64 (1) + sql_forth + check one_row: sql_after end + end + sql_finalize + end + +feature {NONE} -- SQL select + + sql_last_insert_temp_user_id: STRING = "SELECT MAX(uid) FROM auth_temp_users;" + + + Select_user_auth_temp_by_id: STRING = "SELECT uid, name, password, salt, email, application FROM auth_temp_users as u where uid=:uid;" + + + sql_insert_temp_user: STRING = "INSERT INTO auth_temp_users (name, password, salt, email, application) VALUES (:name, :password, :salt, :email, :application);" + -- SQL Insert to add a new user. + + select_temp_user_by_name: STRING = "SELECT uid, name, password, salt, email, application FROM auth_temp_users WHERE name =:name;" + -- Retrieve user by name if exists. + + select_temp_user_by_email: STRING = "SELECT uid, name, password, salt, email, application FROM auth_temp_users WHERE email =:email;" + -- Retrieve user by email if exists. + + select_temp_user_by_activation_token: STRING = "SELECT u.uid, u.name, u.password, u.salt, u.email, u.application FROM auth_temp_users as u JOIN users_activations as ua ON ua.uid = u.uid and ua.token = :token;" + -- Retrieve user by activation token if exist. + + sql_delete_temp_user: STRING = "DELETE FROM auth_temp_users WHERE uid=:uid;" + + select_temp_users_count: STRING = "SELECT count(*) FROM auth_temp_users;" + -- Number of temporal users. + + sql_select_temp_recent_users: STRING = "SELECT uid, name, password, salt, email, application FROM auth_temp_users ORDER BY uid DESC LIMIT :rows OFFSET :offset ;" + -- Retrieve recent users + + select_token_activation_by_user_id: STRING = "SELECT token FROM users_activations WHERE uid = :uid;" + + note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/service/cms_api.e b/src/service/cms_api.e index 5fd32d5..cce86f2 100644 --- a/src/service/cms_api.e +++ b/src/service/cms_api.e @@ -316,8 +316,16 @@ feature -- Emails new_email (a_to_address: READABLE_STRING_8; a_subject: READABLE_STRING_8; a_content: READABLE_STRING_8): CMS_EMAIL -- New email object. + local + l_subject: READABLE_STRING_8 do - create Result.make (setup.site_email, a_to_address, a_subject, a_content) + l_subject := a_subject + if attached setup.site_email_subject_prefix as l_prefix then + if not l_subject.starts_with (l_prefix) then + l_subject := l_prefix + l_subject + end + end + create Result.make (setup.site_email, a_to_address, l_subject, a_content) end process_email (e: CMS_EMAIL) diff --git a/src/service/user/cms_user_api.e b/src/service/user/cms_user_api.e index 956afd5..7bd9b94 100644 --- a/src/service/user/cms_user_api.e +++ b/src/service/user/cms_user_api.e @@ -251,7 +251,6 @@ feature -- Change User do reset_error if - attached a_user.password as l_password and then attached a_user.email as l_email then storage.new_user (a_user) @@ -289,12 +288,6 @@ feature -- User Activation storage.save_activation (a_token, a_id) end - remove_activation (a_token: READABLE_STRING_32) - -- Remove activation token `a_token', from the storage. - do - storage.remove_activation (a_token) - end - feature -- User Password Recovery new_password (a_token: READABLE_STRING_32; a_id: INTEGER_64) @@ -320,7 +313,101 @@ feature -- User status Trashed: INTEGER = -1 -- The user is trashed (soft delete), ready to be deleted/destroyed from storage. +feature -- Access - Temp User + + temp_users_count: INTEGER + -- Number of pending users. + --! to be accepted or rehected + do + Result := storage.temp_users_count + end + + temp_user_by_name (a_username: READABLE_STRING_GENERAL): detachable CMS_USER + -- User by name `a_user_name', if any. + do + Result := storage.temp_user_by_name (a_username.as_string_32) + end + + temp_user_by_email (a_email: READABLE_STRING_8): detachable CMS_USER + -- User by email `a_email', if any. + do + Result := storage.temp_user_by_email (a_email) + end + + temp_user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- User by activation token `a_token'. + do + Result := storage.temp_user_by_activation_token (a_token) + end + + temp_recent_users (params: CMS_DATA_QUERY_PARAMETERS): ITERABLE [CMS_TEMP_USER] + -- List of the `a_rows' most recent users starting from `a_offset'. + do + Result := storage.temp_recent_users (params.offset.to_integer_32, params.size.to_integer_32) + end + + token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING + do + Result := storage.token_by_temp_user_id (a_id) + end + +feature -- Change Temp User + + new_user_from_temp_user (a_user: CMS_TEMP_USER) + -- Add a new user `a_user'. + require + no_id: not a_user.has_id + has_hashed_password: a_user.hashed_password /= Void + has_sal: a_user.salt /= Void + do + reset_error + if + attached a_user.hashed_password as l_password and then + attached a_user.salt as l_salt and then + attached a_user.email as l_email + then + storage.new_user_from_temp_user (a_user) + error_handler.append (storage.error_handler) + else + error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!") + end + end + + new_temp_user (a_user: CMS_TEMP_USER) + -- Add a new user `a_user'. + require + no_id: not a_user.has_id + no_hashed_password: a_user.hashed_password = Void + do + reset_error + if + attached a_user.password as l_password and then + attached a_user.email as l_email + then + storage.new_temp_user (a_user) + error_handler.append (storage.error_handler) + else + error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!") + end + end + + remove_activation (a_token: READABLE_STRING_32) + -- Remove activation token `a_token', from the storage. + do + storage.remove_activation (a_token) + end + + delete_temp_user (a_user: CMS_TEMP_USER) + -- Delete user `a_user'. + require + has_id: a_user.has_id + do + reset_error + storage.delete_temp_user (a_user) + error_handler.append (storage.error_handler) + end + note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end