mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralsec.git
				synced 2025-10-30 02:12:32 +00:00 
			
		
		
		
	Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
This commit is contained in:
		| @@ -124,7 +124,7 @@ add_executable( owsec | |||||||
|         src/storage/orm_actionLinks.cpp src/storage/orm_actionLinks.h |         src/storage/orm_actionLinks.cpp src/storage/orm_actionLinks.h | ||||||
|         src/storage/orm_avatar.cpp src/storage/orm_avatar.h |         src/storage/orm_avatar.cpp src/storage/orm_avatar.h | ||||||
|         src/SpecialUserHelpers.h |         src/SpecialUserHelpers.h | ||||||
|         src/RESTAPI/RESTAPI_db_helpers.h src/storage/orm_logins.cpp src/storage/orm_logins.h src/RESTAPI/RESTAPI_totp_handler.cpp src/RESTAPI/RESTAPI_totp_handler.h src/TotpCache.h src/RESTAPI/RESTAPI_subtotp_handler.cpp src/RESTAPI/RESTAPI_subtotp_handler.h src/RESTAPI/RESTAPI_signup_handler.cpp src/RESTAPI/RESTAPI_signup_handler.h) |         src/RESTAPI/RESTAPI_db_helpers.h src/storage/orm_logins.cpp src/storage/orm_logins.h src/RESTAPI/RESTAPI_totp_handler.cpp src/RESTAPI/RESTAPI_totp_handler.h src/TotpCache.h src/RESTAPI/RESTAPI_subtotp_handler.cpp src/RESTAPI/RESTAPI_subtotp_handler.h src/RESTAPI/RESTAPI_signup_handler.cpp src/RESTAPI/RESTAPI_signup_handler.h src/MessagingTemplates.cpp src/MessagingTemplates.h) | ||||||
|  |  | ||||||
| if(NOT SMALL_BUILD) | if(NOT SMALL_BUILD) | ||||||
|     target_link_libraries(owsec PUBLIC |     target_link_libraries(owsec PUBLIC | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								OPERATOR.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								OPERATOR.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | # Operator Support | ||||||
|  | In order to support multiple tenants and operators, you must prepare the security service to serve | ||||||
|  | customized e-mails and messages. | ||||||
|  |  | ||||||
|  | ## Structure for `templates` | ||||||
|  | Any file in the root of the directory will be used as defaults. The following files must be present: | ||||||
|  | - email_invitation.html/txt : This email message will be sent to a newly added user.  | ||||||
|  | - email_verification.html/txt : This email is sent when an email verification is required. | ||||||
|  | - password_reset.html/txt : This is sent when a pasword reset is requested. | ||||||
|  | - verification_code.html/txt : This is used during MFA when email based. | ||||||
|  | - signup_verification.html/txt : This email is send to a new subscriber who signed up for service. | ||||||
|  | - sub_email_verification.html/txt : This is sent to a subscriber requiring an email verification. | ||||||
|  | - sub_verification_code.html/txt : This is used during MFA when email based for a subscriber. | ||||||
|  | - logo.jpg : The default logo to use in any of these emails. | ||||||
|  |  | ||||||
|  | ## Structure for `wwwassets` | ||||||
|  | Any file in the root of the directory will be used as defaults. The following files must be present: | ||||||
|  | - email_verification_error.html : Used when email verification has failed. | ||||||
|  | - email_verification_success.html : Used when emil verification has succeeded. | ||||||
|  | - invitation_error.html : | ||||||
|  | - invitation_success.html : | ||||||
|  | - password_policy.html : | ||||||
|  | - password_reset.html : | ||||||
|  | - password_reset_success.html : | ||||||
|  | - password_reset_error.html : | ||||||
|  | - signup_verification.html : | ||||||
|  | - signup_verification_error.html : | ||||||
|  | - signup_verification_success.html : | ||||||
|  | - favicon.ico : icon for the application | ||||||
|  | - 404_error.html : your customized 404 page | ||||||
|  | - the_logo : the logo to use. | ||||||
|  |  | ||||||
|  | ## For tenants | ||||||
|  | When creating a tenant/operator, you must create a subdirectory inside each `wwwassets` and `templates` and replicate  | ||||||
|  | all the files that appear at the root level. You need to use the short Operator name (also known as RegistrantId in the API). This means  | ||||||
|  | no spaces, all lowercase characters and numbers. No special characters: 0-9 and a-z.  | ||||||
|  |  | ||||||
| @@ -5,6 +5,7 @@ | |||||||
| #include "ActionLinkManager.h" | #include "ActionLinkManager.h" | ||||||
| #include "StorageService.h" | #include "StorageService.h" | ||||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||||
|  | #include "MessagingTemplates.h" | ||||||
|  |  | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|  |  | ||||||
| @@ -61,7 +62,7 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|                 switch(i.action) { |                 switch(i.action) { | ||||||
|                     case OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD: { |                     case OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD: { | ||||||
|                             if(AuthService::SendEmailToUser(i.id, UInfo.email, AuthService::FORGOT_PASSWORD)) { |                             if(AuthService::SendEmailToUser(i.id, UInfo.email, MessagingTemplates::FORGOT_PASSWORD)) { | ||||||
|                                 Logger().information(fmt::format("Send password reset link to {}",UInfo.email)); |                                 Logger().information(fmt::format("Send password reset link to {}",UInfo.email)); | ||||||
|                             } |                             } | ||||||
|                             StorageService()->ActionLinksDB().SentAction(i.id); |                             StorageService()->ActionLinksDB().SentAction(i.id); | ||||||
| @@ -69,15 +70,24 @@ namespace OpenWifi { | |||||||
|                         break; |                         break; | ||||||
|  |  | ||||||
|                     case OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL: { |                     case OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL: { | ||||||
|                             if(AuthService::SendEmailToUser(i.id, UInfo.email, AuthService::EMAIL_VERIFICATION)) { |                             if(AuthService::SendEmailToUser(i.id, UInfo.email, MessagingTemplates::EMAIL_VERIFICATION)) { | ||||||
|                                 Logger().information(fmt::format("Send email verification link to {}",UInfo.email)); |                                 Logger().information(fmt::format("Send email verification link to {}",UInfo.email)); | ||||||
|                             } |                             } | ||||||
|                             StorageService()->ActionLinksDB().SentAction(i.id); |                             StorageService()->ActionLinksDB().SentAction(i.id); | ||||||
|                         } |                         } | ||||||
|                         break; |                         break; | ||||||
|  |  | ||||||
|  |                     case OpenWifi::SecurityObjects::LinkActions::EMAIL_INVITATION: { | ||||||
|  |                             if(AuthService::SendEmailToUser(i.id, UInfo.email, MessagingTemplates::EMAIL_INVITATION)) { | ||||||
|  |                                 Logger().information(fmt::format("Send new subscriber email invitation link to {}",UInfo.email)); | ||||||
|  |                             } | ||||||
|  |                             StorageService()->ActionLinksDB().SentAction(i.id); | ||||||
|  |                         } | ||||||
|  |                         break; | ||||||
|  |  | ||||||
|                     case OpenWifi::SecurityObjects::LinkActions::SUB_FORGOT_PASSWORD: { |                     case OpenWifi::SecurityObjects::LinkActions::SUB_FORGOT_PASSWORD: { | ||||||
|                             if(AuthService::SendEmailToSubUser(i.id, UInfo.email, AuthService::FORGOT_PASSWORD)) { |                             auto Signup = Poco::StringTokenizer(UInfo.signingUp,":"); | ||||||
|  |                             if(AuthService::SendEmailToSubUser(i.id, UInfo.email,MessagingTemplates::SUB_FORGOT_PASSWORD, Signup.count()==1 ? "" : Signup[0])) { | ||||||
|                                 Logger().information(fmt::format("Send subscriber password reset link to {}",UInfo.email)); |                                 Logger().information(fmt::format("Send subscriber password reset link to {}",UInfo.email)); | ||||||
|                             } |                             } | ||||||
|                             StorageService()->ActionLinksDB().SentAction(i.id); |                             StorageService()->ActionLinksDB().SentAction(i.id); | ||||||
| @@ -85,7 +95,8 @@ namespace OpenWifi { | |||||||
|                         break; |                         break; | ||||||
|  |  | ||||||
|                     case OpenWifi::SecurityObjects::LinkActions::SUB_VERIFY_EMAIL: { |                     case OpenWifi::SecurityObjects::LinkActions::SUB_VERIFY_EMAIL: { | ||||||
|                             if(AuthService::SendEmailToSubUser(i.id, UInfo.email, AuthService::EMAIL_VERIFICATION)) { |                             auto Signup = Poco::StringTokenizer(UInfo.signingUp,":"); | ||||||
|  |                             if(AuthService::SendEmailToSubUser(i.id, UInfo.email, MessagingTemplates::SUB_EMAIL_VERIFICATION, Signup.count()==1 ? "" : Signup[0])) { | ||||||
|                                 Logger().information(fmt::format("Send subscriber email verification link to {}",UInfo.email)); |                                 Logger().information(fmt::format("Send subscriber email verification link to {}",UInfo.email)); | ||||||
|                             } |                             } | ||||||
|                             StorageService()->ActionLinksDB().SentAction(i.id); |                             StorageService()->ActionLinksDB().SentAction(i.id); | ||||||
| @@ -93,21 +104,14 @@ namespace OpenWifi { | |||||||
|                         break; |                         break; | ||||||
|  |  | ||||||
|                     case OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP: { |                     case OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP: { | ||||||
|                         if(AuthService::SendEmailToSubUser(i.id, UInfo.email, AuthService::SIGNUP_VERIFICATION)) { |                         auto Signup = Poco::StringTokenizer(UInfo.signingUp,":"); | ||||||
|  |                         if(AuthService::SendEmailToSubUser(i.id, UInfo.email, MessagingTemplates::SIGNUP_VERIFICATION, Signup.count()==1 ? "" : Signup[0])) { | ||||||
|                             Logger().information(fmt::format("Send new subscriber email verification link to {}",UInfo.email)); |                             Logger().information(fmt::format("Send new subscriber email verification link to {}",UInfo.email)); | ||||||
|                         } |                         } | ||||||
|                         StorageService()->ActionLinksDB().SentAction(i.id); |                         StorageService()->ActionLinksDB().SentAction(i.id); | ||||||
|                         } |                         } | ||||||
|                         break; |                         break; | ||||||
|  |  | ||||||
|                     case OpenWifi::SecurityObjects::LinkActions::EMAIL_INVITATION: { |  | ||||||
|                         if(AuthService::SendEmailToSubUser(i.id, UInfo.email, AuthService::EMAIL_INVITATION)) { |  | ||||||
|                             Logger().information(fmt::format("Send new subscriber email invitation link to {}",UInfo.email)); |  | ||||||
|                         } |  | ||||||
|                         StorageService()->ActionLinksDB().SentAction(i.id); |  | ||||||
|                     } |  | ||||||
|                         break; |  | ||||||
|  |  | ||||||
|                     default: { |                     default: { | ||||||
|                         StorageService()->ActionLinksDB().SentAction(i.id); |                         StorageService()->ActionLinksDB().SentAction(i.id); | ||||||
|                     } |                     } | ||||||
|   | |||||||
| @@ -20,14 +20,10 @@ | |||||||
|  |  | ||||||
| #include "SMTPMailerService.h" | #include "SMTPMailerService.h" | ||||||
| #include "MFAServer.h" | #include "MFAServer.h" | ||||||
|  | #include "MessagingTemplates.h" | ||||||
|  |  | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|  |  | ||||||
|     inline const static std::vector<std::string>  EmailTemplateNames = {  "password_reset" , |  | ||||||
|                                                                           "email_verification", |  | ||||||
|                                                                           "signuo_verification", |  | ||||||
|                                                                           "email_invitation" }; |  | ||||||
|  |  | ||||||
|     AuthService::ACCESS_TYPE AuthService::IntToAccessType(int C) { |     AuthService::ACCESS_TYPE AuthService::IntToAccessType(int C) { | ||||||
| 		switch (C) { | 		switch (C) { | ||||||
| 		case 1: return USERNAME; | 		case 1: return USERNAME; | ||||||
| @@ -38,7 +34,6 @@ namespace OpenWifi { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
| 	int AuthService::AccessTypeToInt(ACCESS_TYPE T) { | 	int AuthService::AccessTypeToInt(ACCESS_TYPE T) { | ||||||
| 		switch (T) { | 		switch (T) { | ||||||
| 		case USERNAME: return 1; | 		case USERNAME: return 1; | ||||||
| @@ -519,7 +514,6 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|             return SUCCESS; |             return SUCCESS; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return INVALID_CREDENTIALS; |         return INVALID_CREDENTIALS; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -569,40 +563,62 @@ namespace OpenWifi { | |||||||
|         return INVALID_CREDENTIALS; |         return INVALID_CREDENTIALS; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool AuthService::SendEmailToUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason) { |     bool AuthService::SendEmailChallengeCode(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Challenge) { | ||||||
|  |         auto OperatorParts = Poco::StringTokenizer(UInfo.userinfo.signingUp,":"); | ||||||
|  |         if(UInfo.userinfo.signingUp.empty() || OperatorParts.count()!=2) { | ||||||
|  |             MessageAttributes Attrs; | ||||||
|  |             Attrs[RECIPIENT_EMAIL] = UInfo.userinfo.email; | ||||||
|  |             Attrs[LOGO] = AuthService::GetLogoAssetURI(); | ||||||
|  |             Attrs[SUBJECT] = "Login validation code"; | ||||||
|  |             Attrs[CHALLENGE_CODE] = Challenge; | ||||||
|  |             return SMTPMailerService()->SendMessage(UInfo.userinfo.email, MessagingTemplates::TemplateName(MessagingTemplates::VERIFICATION_CODE), Attrs); | ||||||
|  |         } else { | ||||||
|  |             MessageAttributes Attrs; | ||||||
|  |             Attrs[RECIPIENT_EMAIL] = UInfo.userinfo.email; | ||||||
|  |             Attrs[LOGO] = AuthService::GetLogoAssetURI(); | ||||||
|  |             Attrs[SUBJECT] = "Login validation code"; | ||||||
|  |             Attrs[CHALLENGE_CODE] = Challenge; | ||||||
|  |             return SMTPMailerService()->SendMessage(UInfo.userinfo.email, MessagingTemplates::TemplateName(MessagingTemplates::SUB_VERIFICATION_CODE,OperatorParts[0]), Attrs); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool AuthService::SendEmailToUser(const std::string &LinkId, std::string &Email, MessagingTemplates::EMAIL_REASON Reason) { | ||||||
|         SecurityObjects::UserInfo   UInfo; |         SecurityObjects::UserInfo   UInfo; | ||||||
|  |  | ||||||
|         if(StorageService()->UserDB().GetUserByEmail(Email,UInfo)) { |         if(StorageService()->UserDB().GetUserByEmail(Email,UInfo)) { | ||||||
|             switch (Reason) { |             switch (Reason) { | ||||||
|  |  | ||||||
|                 case FORGOT_PASSWORD: { |                 case MessagingTemplates::FORGOT_PASSWORD: { | ||||||
|                         MessageAttributes Attrs; |                         MessageAttributes Attrs; | ||||||
|                         Attrs[RECIPIENT_EMAIL] = UInfo.email; |                         Attrs[RECIPIENT_EMAIL] = UInfo.email; | ||||||
|                         Attrs[LOGO] = GetLogoAssetURI(); |                         Attrs[LOGO] = GetLogoAssetURI(); | ||||||
|                         Attrs[SUBJECT] = "Password reset link"; |                         Attrs[SUBJECT] = "Password reset link"; | ||||||
|                         Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + LinkId ; |                         Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + LinkId ; | ||||||
|                         SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[FORGOT_PASSWORD], Attrs); |                         Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=password_reset&id=" + LinkId ; | ||||||
|  |                         SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::FORGOT_PASSWORD), Attrs); | ||||||
|                     } |                     } | ||||||
|                     break; |                     break; | ||||||
|  |  | ||||||
|                 case EMAIL_VERIFICATION: { |                 case MessagingTemplates::EMAIL_VERIFICATION: { | ||||||
|                         MessageAttributes Attrs; |                         MessageAttributes Attrs; | ||||||
|                         Attrs[RECIPIENT_EMAIL] = UInfo.email; |                         Attrs[RECIPIENT_EMAIL] = UInfo.email; | ||||||
|                         Attrs[LOGO] = GetLogoAssetURI(); |                         Attrs[LOGO] = GetLogoAssetURI(); | ||||||
|                         Attrs[SUBJECT] = "e-mail Address Verification"; |                         Attrs[SUBJECT] = "e-mail Address Verification"; | ||||||
|                         Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + LinkId ; |                         Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + LinkId ; | ||||||
|                         SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[EMAIL_VERIFICATION], Attrs); |                         Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=email_verification&id=" + LinkId ; | ||||||
|  |                         SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::EMAIL_VERIFICATION), Attrs); | ||||||
|                         UInfo.waitingForEmailCheck = true; |                         UInfo.waitingForEmailCheck = true; | ||||||
|                     } |                     } | ||||||
|                     break; |                     break; | ||||||
|  |  | ||||||
|                 case EMAIL_INVITATION: { |                 case MessagingTemplates::EMAIL_INVITATION: { | ||||||
|                     MessageAttributes Attrs; |                     MessageAttributes Attrs; | ||||||
|                     Attrs[RECIPIENT_EMAIL] = UInfo.email; |                     Attrs[RECIPIENT_EMAIL] = UInfo.email; | ||||||
|                     Attrs[LOGO] = GetLogoAssetURI(); |                     Attrs[LOGO] = GetLogoAssetURI(); | ||||||
|                     Attrs[SUBJECT] = "e-mail Invitation"; |                     Attrs[SUBJECT] = "e-mail Invitation"; | ||||||
|                     Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_invitation&id=" + LinkId ; |                     Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_invitation&id=" + LinkId ; | ||||||
|                     SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[EMAIL_INVITATION], Attrs); |                     Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=email_invitation&id=" + LinkId ; | ||||||
|  |                     SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::EMAIL_INVITATION), Attrs); | ||||||
|                     UInfo.waitingForEmailCheck = true; |                     UInfo.waitingForEmailCheck = true; | ||||||
|                     } |                     } | ||||||
|                     break; |                     break; | ||||||
| @@ -615,40 +631,43 @@ namespace OpenWifi { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool AuthService::SendEmailToSubUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason) { |     bool AuthService::SendEmailToSubUser(const std::string &LinkId, std::string &Email, MessagingTemplates::EMAIL_REASON Reason, const std::string &OperatorName ) { | ||||||
|         SecurityObjects::UserInfo   UInfo; |         SecurityObjects::UserInfo   UInfo; | ||||||
|  |  | ||||||
|         if(StorageService()->SubDB().GetUserByEmail(Email,UInfo)) { |         if(StorageService()->SubDB().GetUserByEmail(Email,UInfo)) { | ||||||
|             switch (Reason) { |             switch (Reason) { | ||||||
|  |  | ||||||
|                 case FORGOT_PASSWORD: { |                 case MessagingTemplates::SUB_FORGOT_PASSWORD: { | ||||||
|                     MessageAttributes Attrs; |                     MessageAttributes Attrs; | ||||||
|                     Attrs[RECIPIENT_EMAIL] = UInfo.email; |                     Attrs[RECIPIENT_EMAIL] = UInfo.email; | ||||||
|                     Attrs[LOGO] = GetLogoAssetURI(); |                     Attrs[LOGO] = GetLogoAssetURI(); | ||||||
|                     Attrs[SUBJECT] = "Password reset link"; |                     Attrs[SUBJECT] = "Password reset link"; | ||||||
|                     Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + LinkId ; |                     Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=sub_password_reset&id=" + LinkId ; | ||||||
|                     SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[FORGOT_PASSWORD], Attrs); |                     Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=sub_password_reset&id=" + LinkId ; | ||||||
|  |                     SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::SUB_FORGOT_PASSWORD, OperatorName), Attrs); | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|                 case EMAIL_VERIFICATION: { |                 case MessagingTemplates::SUB_EMAIL_VERIFICATION: { | ||||||
|                     MessageAttributes Attrs; |                     MessageAttributes Attrs; | ||||||
|                     Attrs[RECIPIENT_EMAIL] = UInfo.email; |                     Attrs[RECIPIENT_EMAIL] = UInfo.email; | ||||||
|                     Attrs[LOGO] = GetLogoAssetURI(); |                     Attrs[LOGO] = GetLogoAssetURI(); | ||||||
|                     Attrs[SUBJECT] = "e-mail Address Verification"; |                     Attrs[SUBJECT] = "e-mail Address Verification"; | ||||||
|                     Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + LinkId ; |                     Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=sub_email_verification&id=" + LinkId ; | ||||||
|                     SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[EMAIL_VERIFICATION], Attrs); |                     Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=sub_email_verification&id=" + LinkId ; | ||||||
|  |                     SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::SUB_EMAIL_VERIFICATION, OperatorName), Attrs); | ||||||
|                     UInfo.waitingForEmailCheck = true; |                     UInfo.waitingForEmailCheck = true; | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|                 case SIGNUP_VERIFICATION: { |                 case MessagingTemplates::SIGNUP_VERIFICATION: { | ||||||
|                     MessageAttributes Attrs; |                     MessageAttributes Attrs; | ||||||
|                     Attrs[RECIPIENT_EMAIL] = UInfo.email; |                     Attrs[RECIPIENT_EMAIL] = UInfo.email; | ||||||
|                     Attrs[LOGO] = GetLogoAssetURI(); |                     Attrs[LOGO] = GetLogoAssetURI(); | ||||||
|                     Attrs[SUBJECT] = "Signup e-mail Address Verification"; |                     Attrs[SUBJECT] = "Signup e-mail Address Verification"; | ||||||
|                     Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=signup_verification&id=" + LinkId ; |                     Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=signup_verification&id=" + LinkId ; | ||||||
|                     SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[SIGNUP_VERIFICATION], Attrs); |                     Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=signup_verification&id=" + LinkId ; | ||||||
|  |                     SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::SIGNUP_VERIFICATION, OperatorName), Attrs); | ||||||
|                     UInfo.waitingForEmailCheck = true; |                     UInfo.waitingForEmailCheck = true; | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ | |||||||
|  |  | ||||||
| #include "framework/MicroService.h" | #include "framework/MicroService.h" | ||||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||||
|  | #include "MessagingTemplates.h" | ||||||
|  |  | ||||||
| namespace OpenWifi{ | namespace OpenWifi{ | ||||||
|  |  | ||||||
| @@ -36,13 +37,6 @@ namespace OpenWifi{ | |||||||
|             CUSTOM |             CUSTOM | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         enum EMAIL_REASON { |  | ||||||
|             FORGOT_PASSWORD = 0, |  | ||||||
|             EMAIL_VERIFICATION, |  | ||||||
|             SIGNUP_VERIFICATION, |  | ||||||
|             EMAIL_INVITATION |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         static ACCESS_TYPE IntToAccessType(int C); |         static ACCESS_TYPE IntToAccessType(int C); | ||||||
|         static int AccessTypeToInt(ACCESS_TYPE T); |         static int AccessTypeToInt(ACCESS_TYPE T); | ||||||
|  |  | ||||||
| @@ -91,10 +85,12 @@ namespace OpenWifi{ | |||||||
|         [[nodiscard]] static bool VerifyEmail(SecurityObjects::UserInfo &UInfo); |         [[nodiscard]] static bool VerifyEmail(SecurityObjects::UserInfo &UInfo); | ||||||
|         [[nodiscard]] static bool VerifySubEmail(SecurityObjects::UserInfo &UInfo); |         [[nodiscard]] static bool VerifySubEmail(SecurityObjects::UserInfo &UInfo); | ||||||
|  |  | ||||||
|         [[nodiscard]] static bool SendEmailToUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason); |         [[nodiscard]] static bool SendEmailToUser(const std::string &LinkId, std::string &Email, MessagingTemplates::EMAIL_REASON Reason); | ||||||
|         [[nodiscard]] static bool SendEmailToSubUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason); |         [[nodiscard]] static bool SendEmailToSubUser(const std::string &LinkId, std::string &Email, MessagingTemplates::EMAIL_REASON Reason, const std::string &OperatorName); | ||||||
|         [[nodiscard]] bool RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo); |         [[nodiscard]] bool RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo); | ||||||
|  |  | ||||||
|  |         [[nodiscard]] bool SendEmailChallengeCode(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &code); | ||||||
|  |  | ||||||
|         bool DeleteUserFromCache(const std::string &UserName); |         bool DeleteUserFromCache(const std::string &UserName); | ||||||
|         bool DeleteSubUserFromCache(const std::string &UserName); |         bool DeleteSubUserFromCache(const std::string &UserName); | ||||||
|         void RevokeToken(std::string & Token); |         void RevokeToken(std::string & Token); | ||||||
|   | |||||||
| @@ -44,12 +44,7 @@ namespace OpenWifi { | |||||||
|             std::string Message = "This is your login code: " + Challenge + " Please enter this in your login screen."; |             std::string Message = "This is your login code: " + Challenge + " Please enter this in your login screen."; | ||||||
|             return SMSSender()->Send(UInfo.userinfo.userTypeProprietaryInfo.mobiles[0].number, Message); |             return SMSSender()->Send(UInfo.userinfo.userTypeProprietaryInfo.mobiles[0].number, Message); | ||||||
|         } else if(Method==MFAMETHODS::EMAIL && SMTPMailerService()->Enabled() && !UInfo.userinfo.email.empty()) { |         } else if(Method==MFAMETHODS::EMAIL && SMTPMailerService()->Enabled() && !UInfo.userinfo.email.empty()) { | ||||||
|             MessageAttributes Attrs; |             return AuthService()->SendEmailChallengeCode(UInfo,Challenge); | ||||||
|             Attrs[RECIPIENT_EMAIL] = UInfo.userinfo.email; |  | ||||||
|             Attrs[LOGO] = AuthService::GetLogoAssetURI(); |  | ||||||
|             Attrs[SUBJECT] = "Login validation code"; |  | ||||||
|             Attrs[CHALLENGE_CODE] = Challenge; |  | ||||||
|             return SMTPMailerService()->SendMessage(UInfo.userinfo.email, "verification_code.txt", Attrs); |  | ||||||
|         } else if(Method==MFAMETHODS::AUTHENTICATOR && !UInfo.userinfo.userTypeProprietaryInfo.authenticatorSecret.empty()) { |         } else if(Method==MFAMETHODS::AUTHENTICATOR && !UInfo.userinfo.userTypeProprietaryInfo.authenticatorSecret.empty()) { | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								src/MessagingTemplates.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/MessagingTemplates.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2022-07-25. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include "MessagingTemplates.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  | } // OpenWifi | ||||||
							
								
								
									
										75
									
								
								src/MessagingTemplates.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/MessagingTemplates.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2022-07-25. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |  | ||||||
|  |     class MessagingTemplates { | ||||||
|  |     public: | ||||||
|  |         static MessagingTemplates & instance() { | ||||||
|  |             static auto instance = new MessagingTemplates; | ||||||
|  |             return *instance; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         enum EMAIL_REASON { | ||||||
|  |             FORGOT_PASSWORD = 0, | ||||||
|  |             EMAIL_VERIFICATION, | ||||||
|  |             SIGNUP_VERIFICATION, | ||||||
|  |             EMAIL_INVITATION, | ||||||
|  |             VERIFICATION_CODE, | ||||||
|  |             SUB_FORGOT_PASSWORD, | ||||||
|  |             SUB_EMAIL_VERIFICATION, | ||||||
|  |             SUB_VERIFICATION_CODE | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         static std::string AddOperator(const std::string & filename, const std::string &OperatorName) { | ||||||
|  |             if(OperatorName.empty()) | ||||||
|  |                 return "/" + filename; | ||||||
|  |             return "/" + OperatorName + "/" + filename; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         static std::string TemplateName( EMAIL_REASON r , const std::string &OperatorName="") { | ||||||
|  |             switch (r) { | ||||||
|  |                 case FORGOT_PASSWORD: return AddOperator(EmailTemplateNames[FORGOT_PASSWORD],OperatorName); | ||||||
|  |                 case EMAIL_VERIFICATION: return AddOperator(EmailTemplateNames[EMAIL_VERIFICATION],OperatorName); | ||||||
|  |                 case SIGNUP_VERIFICATION: return AddOperator(EmailTemplateNames[SIGNUP_VERIFICATION],OperatorName); | ||||||
|  |                 case EMAIL_INVITATION: return AddOperator(EmailTemplateNames[EMAIL_INVITATION],OperatorName); | ||||||
|  |                 case VERIFICATION_CODE: return AddOperator(EmailTemplateNames[VERIFICATION_CODE],OperatorName); | ||||||
|  |                 case SUB_FORGOT_PASSWORD: return AddOperator(EmailTemplateNames[SUB_FORGOT_PASSWORD],OperatorName); | ||||||
|  |                 case SUB_EMAIL_VERIFICATION: return AddOperator(EmailTemplateNames[SUB_EMAIL_VERIFICATION],OperatorName); | ||||||
|  |                 case SUB_VERIFICATION_CODE: return AddOperator(EmailTemplateNames[SUB_VERIFICATION_CODE],OperatorName); | ||||||
|  |                 default: | ||||||
|  |                     return ""; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         static std::string Logo(const std::string &OperatorName = "" ) { | ||||||
|  |             return AddOperator("logo.jpg", OperatorName); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         static std::string SubLogo(const std::string &OperatorName = "" ) { | ||||||
|  |             return AddOperator("sub_logo.jpg", OperatorName); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         inline const static std::vector<std::string>  EmailTemplateNames = { | ||||||
|  |                 "password_reset", | ||||||
|  |                 "email_verification", | ||||||
|  |                 "signup_verification", | ||||||
|  |                 "email_invitation", | ||||||
|  |                 "verification_code", | ||||||
|  |                 "sub_password_reset", | ||||||
|  |                 "sub_email_verification", | ||||||
|  |                 "sub_verification_code" | ||||||
|  |         }; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     inline MessagingTemplates & MessagingTemplates() { return MessagingTemplates::instance(); } | ||||||
|  |  | ||||||
|  | } // OpenWifi | ||||||
|  |  | ||||||
| @@ -23,8 +23,12 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|         if(Action=="password_reset") |         if(Action=="password_reset") | ||||||
|             return RequestResetPassword(Link); |             return RequestResetPassword(Link); | ||||||
|  |         else if(Action=="sub_password_reset") | ||||||
|  |             return RequestSubResetPassword(Link); | ||||||
|         else if(Action=="email_verification") |         else if(Action=="email_verification") | ||||||
|             return DoEmailVerification(Link); |             return DoEmailVerification(Link); | ||||||
|  |         else if(Action=="sub_email_verification") | ||||||
|  |             return DoSubEmailVerification(Link); | ||||||
|         else if(Action=="signup_verification") |         else if(Action=="signup_verification") | ||||||
|             return DoNewSubVerification(Link); |             return DoNewSubVerification(Link); | ||||||
|         else |         else | ||||||
| @@ -36,6 +40,8 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|         if(Action=="password_reset") |         if(Action=="password_reset") | ||||||
|             return CompleteResetPassword(); |             return CompleteResetPassword(); | ||||||
|  |         else if(Action=="sub_password_reset") | ||||||
|  |             return CompleteResetPassword(); | ||||||
|         else if(Action=="signup_completion") |         else if(Action=="signup_completion") | ||||||
|             return CompleteSubVerification(); |             return CompleteSubVerification(); | ||||||
|         else if(Action=="email_invitation") |         else if(Action=="email_invitation") | ||||||
| @@ -201,10 +207,11 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|             //  Send the update to the provisioning service |             //  Send the update to the provisioning service | ||||||
|             Poco::JSON::Object  Body; |             Poco::JSON::Object  Body; | ||||||
|             Body.set("signupUUID", UInfo.signingUp); |             auto RawSignup = Poco::StringTokenizer(UInfo.signingUp,":"); | ||||||
|  |             Body.set("signupUUID", RawSignup.count()==1 ? UInfo.signingUp : RawSignup[1]); | ||||||
|             OpenAPIRequestPut   ProvRequest(uSERVICE_PROVISIONING,"/api/v1/signup", |             OpenAPIRequestPut   ProvRequest(uSERVICE_PROVISIONING,"/api/v1/signup", | ||||||
|                                             { |                                             { | ||||||
|                                                 {"signupUUID", UInfo.signingUp} , |                                                 {"signupUUID", RawSignup.count()==1 ? UInfo.signingUp : RawSignup[1]} , | ||||||
|                                                 {"operation", "emailVerified"} |                                                 {"operation", "emailVerified"} | ||||||
|                                             }, |                                             }, | ||||||
|                                             Body,30000); |                                             Body,30000); | ||||||
| @@ -269,4 +276,12 @@ namespace OpenWifi { | |||||||
|         /// TODO: |         /// TODO: | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void RESTAPI_action_links::RequestSubResetPassword(SecurityObjects::ActionLink &Link) { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void RESTAPI_action_links::DoSubEmailVerification(SecurityObjects::ActionLink &Link) { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,9 +22,11 @@ namespace OpenWifi { | |||||||
|                                         true, RateLimit{.Interval=1000,.MaxCalls=10}) {} |                                         true, RateLimit{.Interval=1000,.MaxCalls=10}) {} | ||||||
|         static auto PathName() { return std::list<std::string>{"/api/v1/actionLink"}; }; |         static auto PathName() { return std::list<std::string>{"/api/v1/actionLink"}; }; | ||||||
|         void RequestResetPassword(SecurityObjects::ActionLink &Link); |         void RequestResetPassword(SecurityObjects::ActionLink &Link); | ||||||
|  |         void RequestSubResetPassword(SecurityObjects::ActionLink &Link); | ||||||
|         void CompleteResetPassword(); |         void CompleteResetPassword(); | ||||||
|         void CompleteSubVerification(); |         void CompleteSubVerification(); | ||||||
|         void DoEmailVerification(SecurityObjects::ActionLink &Link); |         void DoEmailVerification(SecurityObjects::ActionLink &Link); | ||||||
|  |         void DoSubEmailVerification(SecurityObjects::ActionLink &Link); | ||||||
|         void DoReturnA404(); |         void DoReturnA404(); | ||||||
|         void DoNewSubVerification(SecurityObjects::ActionLink &Link); |         void DoNewSubVerification(SecurityObjects::ActionLink &Link); | ||||||
|         void CompleteEmailInvitation(); |         void CompleteEmailInvitation(); | ||||||
|   | |||||||
| @@ -13,8 +13,9 @@ namespace OpenWifi { | |||||||
|         auto UserName = GetParameter("email"); |         auto UserName = GetParameter("email"); | ||||||
|         auto signupUUID = GetParameter("signupUUID"); |         auto signupUUID = GetParameter("signupUUID"); | ||||||
|         auto owner = GetParameter("owner"); |         auto owner = GetParameter("owner"); | ||||||
|         if(UserName.empty() || signupUUID.empty() || owner.empty()) { |         auto operatorName = GetParameter("operatorName"); | ||||||
|             Logger().error("Signup requires: email, signupUUID, and owner."); |         if(UserName.empty() || signupUUID.empty() || owner.empty() || operatorName.empty()) { | ||||||
|  |             Logger().error("Signup requires: email, signupUUID, operatorName, and owner."); | ||||||
|             return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); |             return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -37,7 +38,7 @@ namespace OpenWifi { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         SecurityObjects::UserInfo   NewSub; |         SecurityObjects::UserInfo   NewSub; | ||||||
|         NewSub.signingUp = signupUUID; |         NewSub.signingUp = operatorName + ":" + signupUUID; | ||||||
|         NewSub.waitingForEmailCheck = true; |         NewSub.waitingForEmailCheck = true; | ||||||
|         NewSub.name = UserName; |         NewSub.name = UserName; | ||||||
|         NewSub.modified = OpenWifi::Now(); |         NewSub.modified = OpenWifi::Now(); | ||||||
|   | |||||||
| @@ -27,7 +27,8 @@ namespace OpenWifi { | |||||||
|         LOGO, |         LOGO, | ||||||
|         TEXT, |         TEXT, | ||||||
|         CHALLENGE_CODE, |         CHALLENGE_CODE, | ||||||
|         SENDER |         SENDER, | ||||||
|  |         ACTION_LINK_HTML | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     static const std::map<MESSAGE_ATTRIBUTES,const std::string> |     static const std::map<MESSAGE_ATTRIBUTES,const std::string> | ||||||
| @@ -44,7 +45,8 @@ namespace OpenWifi { | |||||||
|                                  {  LOGO, "LOGO"}, |                                  {  LOGO, "LOGO"}, | ||||||
|                                  {  TEXT, "TEXT"}, |                                  {  TEXT, "TEXT"}, | ||||||
|                                  {  CHALLENGE_CODE, "CHALLENGE_CODE"}, |                                  {  CHALLENGE_CODE, "CHALLENGE_CODE"}, | ||||||
|                                  {  SENDER, "SENDER"} |                                  {  SENDER, "SENDER"}, | ||||||
|  |                                  {  ACTION_LINK_HTML, "ACTION_LINK_HTML"}, | ||||||
|                                  }; |                                  }; | ||||||
|  |  | ||||||
|     inline const std::string & MessageAttributeToVar(MESSAGE_ATTRIBUTES Attr) { |     inline const std::string & MessageAttributeToVar(MESSAGE_ATTRIBUTES Attr) { | ||||||
|   | |||||||
| @@ -2,7 +2,11 @@ | |||||||
| <html lang="en"> | <html lang="en"> | ||||||
| <head> | <head> | ||||||
|     <meta charset="UTF-8"> |     <meta charset="UTF-8"> | ||||||
|     <title>Title</title> |     <title>eMail Invitation</title> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,6 +4,14 @@ | |||||||
|     <meta charset="UTF-8"> |     <meta charset="UTF-8"> | ||||||
|     <title>Password Reset</title> |     <title>Password Reset</title> | ||||||
|  |  | ||||||
|  |     <form action="${ACTION_LINK_HTML}"> | ||||||
|  |         <label for="fname">First name: </label> | ||||||
|  |         <input type="text" id="fname" name="fname"><br><br> | ||||||
|  |         <label for="lname">Last name: </label> | ||||||
|  |         <input type="text" id="lname" name="lname"><br><br> | ||||||
|  |         <input type="submit" value="Submit"> | ||||||
|  |     </form> | ||||||
|  |  | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										441
									
								
								templates/sample.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								templates/sample.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,441 @@ | |||||||
|  | <!doctype html><html><head> | ||||||
|  |     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="viewport" content="width=device-width"> | ||||||
|  |     <title>Project Groups now available to join - Telecom Infra Project</title> | ||||||
|  |         <style> | ||||||
|  |         /* ------------------------------------- | ||||||
|  |         GLOBAL RESETS | ||||||
|  |         ------------------------------------- */ | ||||||
|  |         /*All the styling goes here*/ | ||||||
|  |         img { | ||||||
|  |         border: none; | ||||||
|  |         -ms-interpolation-mode: bicubic; | ||||||
|  |         max-width: 100%; | ||||||
|  |         } | ||||||
|  |         body { | ||||||
|  |         color: #414141; | ||||||
|  |         background-color: #f6f6f6; | ||||||
|  |         font-family: sans-serif; | ||||||
|  |         -webkit-font-smoothing: antialiased; | ||||||
|  |         font-size: 14px; | ||||||
|  |         line-height: 1.4; | ||||||
|  |         margin: 0; | ||||||
|  |         padding: 0; | ||||||
|  |         -ms-text-size-adjust: 100%; | ||||||
|  |         -webkit-text-size-adjust: 100%; | ||||||
|  |         } | ||||||
|  |         table { | ||||||
|  |         border-collapse: separate; | ||||||
|  |         mso-table-lspace: 0pt; | ||||||
|  |         mso-table-rspace: 0pt; | ||||||
|  |         width: 100%; | ||||||
|  |         text-align: center; | ||||||
|  |         } | ||||||
|  |         table td { | ||||||
|  |         font-family: sans-serif; | ||||||
|  |         font-size: 14px; | ||||||
|  |         color: #414141; | ||||||
|  |         vertical-align: top; | ||||||
|  |         } | ||||||
|  |         /* ------------------------------------- | ||||||
|  |         BODY & CONTAINER | ||||||
|  |         ------------------------------------- */ | ||||||
|  |         .body { | ||||||
|  |         background-color: white; | ||||||
|  |         width: 100%; | ||||||
|  |         } | ||||||
|  |         /* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something | ||||||
|  |         */ | ||||||
|  |         .container { | ||||||
|  |         display: block; | ||||||
|  |         margin: 0 auto !important; | ||||||
|  |         /* makes it centered */ | ||||||
|  |         max-width: 580px; | ||||||
|  |         padding: 10px; | ||||||
|  |         width: 580px; | ||||||
|  |         } | ||||||
|  |         /* This should also be a block element, so that it will fill 100% of the container */ | ||||||
|  |         .content { | ||||||
|  |         box-sizing: border-box; | ||||||
|  |         display: block; | ||||||
|  |         margin: 0 auto; | ||||||
|  |         max-width: 580px; | ||||||
|  |         padding: 10px; | ||||||
|  |         } | ||||||
|  |         /* ------------------------------------- | ||||||
|  |         HEADER, FOOTER, MAIN | ||||||
|  |         ------------------------------------- */ | ||||||
|  |         .main { | ||||||
|  |         background: #ffffff; | ||||||
|  |         border-radius: 0px; | ||||||
|  |         width: 600px; | ||||||
|  |         max-width: 100%; | ||||||
|  |         border: 1px solid #d4d4d4; | ||||||
|  |         padding-left: 45px; | ||||||
|  |         padding-right: 45px; | ||||||
|  |         } | ||||||
|  |         .wrapper { | ||||||
|  |         box-sizing: border-box; | ||||||
|  |         padding: 40px 20px; | ||||||
|  |         } | ||||||
|  |         .content-block { | ||||||
|  |         padding-bottom: 10px; | ||||||
|  |         padding-top: 10px; | ||||||
|  |         } | ||||||
|  |         .footer { | ||||||
|  |         clear: both; | ||||||
|  |         margin-top: 10px; | ||||||
|  |         text-align: center; | ||||||
|  |         width: 100%; | ||||||
|  |         } | ||||||
|  |         .footer td, | ||||||
|  |         .footer p, | ||||||
|  |         .footer span, | ||||||
|  |         .footer a { | ||||||
|  |         color: #999999; | ||||||
|  |         font-size: 12px; | ||||||
|  |         text-align: center; | ||||||
|  |         } | ||||||
|  |         /* ------------------------------------- | ||||||
|  |         TYPOGRAPHY | ||||||
|  |         ------------------------------------- */ | ||||||
|  |         h1, | ||||||
|  |         h2, | ||||||
|  |         h3, | ||||||
|  |         h4 { | ||||||
|  |         color: #414141; | ||||||
|  |         font-family: sans-serif; | ||||||
|  |         font-weight: 400; | ||||||
|  |         line-height: 1.4; | ||||||
|  |         margin: 0; | ||||||
|  |         margin-bottom: 30px; | ||||||
|  |         } | ||||||
|  |         h2 { | ||||||
|  |         font-weight: 600; | ||||||
|  |         } | ||||||
|  |         h1 { | ||||||
|  |         font-size: 35px; | ||||||
|  |         font-weight: 300; | ||||||
|  |         text-align: center; | ||||||
|  |         text-transform: capitalize; | ||||||
|  |         } | ||||||
|  |         p, | ||||||
|  |         ul, | ||||||
|  |         ol { | ||||||
|  |         font-family: sans-serif; | ||||||
|  |         font-size: 14px; | ||||||
|  |         font-weight: normal; | ||||||
|  |         margin: 0; | ||||||
|  |         color: #414141; | ||||||
|  |         margin-bottom: 15px; | ||||||
|  |         } | ||||||
|  |         p li, | ||||||
|  |         ul li, | ||||||
|  |         ol li { | ||||||
|  |         list-style-position: inside; | ||||||
|  |         margin-left: 5px; | ||||||
|  |         color: #414141; | ||||||
|  |         } | ||||||
|  |         a { | ||||||
|  |         color: #29818c !important; | ||||||
|  |         text-decoration: none; | ||||||
|  |         border-bottom: 1px solid #d2d2d2; | ||||||
|  |         } | ||||||
|  |         .footer a { | ||||||
|  |         color: #999999 !important; | ||||||
|  |         } | ||||||
|  |         /* ------------------------------------- | ||||||
|  |         BUTTONS | ||||||
|  |         ------------------------------------- */ | ||||||
|  |         .btn { | ||||||
|  |         box-sizing: border-box; | ||||||
|  |         width: 100%; | ||||||
|  |         } | ||||||
|  |         tbody { | ||||||
|  |         text-align: left; | ||||||
|  |         } | ||||||
|  |         .btn > tbody > tr > td { | ||||||
|  |         padding-bottom: 15px; | ||||||
|  |         } | ||||||
|  |         .btn table { | ||||||
|  |         width: auto; | ||||||
|  |         } | ||||||
|  |         .btn table td { | ||||||
|  |         background-color: #ffffff; | ||||||
|  |         border-radius: 20px; | ||||||
|  |         text-align: center; | ||||||
|  |         } | ||||||
|  |         .btn a { | ||||||
|  |         background-color: #ffffff; | ||||||
|  |         border: solid 1px #489e94; | ||||||
|  |         border-radius: 5px; | ||||||
|  |         box-sizing: border-box; | ||||||
|  |         color: #29818c; | ||||||
|  |         cursor: pointer; | ||||||
|  |         display: inline-block; | ||||||
|  |         font-size: 14px; | ||||||
|  |         font-weight: bold; | ||||||
|  |         margin: 0; | ||||||
|  |         padding: 12px 120px; | ||||||
|  |         text-decoration: none; | ||||||
|  |         font-weight:600; | ||||||
|  |         text-transform: uppercase; | ||||||
|  |         } | ||||||
|  |         .btn-primary table td { | ||||||
|  |         /*  background-color: #29818c;*/ | ||||||
|  |         } | ||||||
|  |         .btn-primary a { | ||||||
|  |         transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, | ||||||
|  |         box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, | ||||||
|  |         border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; | ||||||
|  |         margin: auto; | ||||||
|  |         background-color: #29818c; | ||||||
|  |         border-color: #29818c; | ||||||
|  |         color: #ffffff !important; | ||||||
|  |         } | ||||||
|  |         /* ------------------------------------- | ||||||
|  |         OTHER STYLES THAT MIGHT BE USEFUL | ||||||
|  |         ------------------------------------- */ | ||||||
|  |         .last { | ||||||
|  |         margin-bottom: 0; | ||||||
|  |         } | ||||||
|  |         .first { | ||||||
|  |         margin-top: 0; | ||||||
|  |         } | ||||||
|  |         .align-center { | ||||||
|  |         text-align: center; | ||||||
|  |         } | ||||||
|  |         .align-right { | ||||||
|  |         text-align: right; | ||||||
|  |         } | ||||||
|  |         .align-left { | ||||||
|  |         text-align: left; | ||||||
|  |         } | ||||||
|  |         .clear { | ||||||
|  |         clear: both; | ||||||
|  |         } | ||||||
|  |         .mt0 { | ||||||
|  |         margin-top: 0; | ||||||
|  |         } | ||||||
|  |         .mb0 { | ||||||
|  |         margin-bottom: 0; | ||||||
|  |         } | ||||||
|  |         .preheader { | ||||||
|  |         color: transparent; | ||||||
|  |         display: none; | ||||||
|  |         height: 0; | ||||||
|  |         max-height: 0; | ||||||
|  |         max-width: 0; | ||||||
|  |         opacity: 0; | ||||||
|  |         overflow: hidden; | ||||||
|  |         mso-hide: all; | ||||||
|  |         visibility: hidden; | ||||||
|  |         width: 0; | ||||||
|  |         } | ||||||
|  |         .powered-by a { | ||||||
|  |         text-decoration: none; | ||||||
|  |         } | ||||||
|  |         hr { | ||||||
|  |         border: 0; | ||||||
|  |         border-bottom: 1px solid #d4d4d4; | ||||||
|  |         margin: 20px 0; | ||||||
|  |         } | ||||||
|  |         .grayFont { | ||||||
|  |         color: #999999; | ||||||
|  |         } | ||||||
|  |         .bold { | ||||||
|  |         font-weight: 600; | ||||||
|  |         } | ||||||
|  |         /* ------------------------------------- | ||||||
|  |         RESPONSIVE AND MOBILE FRIENDLY STYLES | ||||||
|  |         ------------------------------------- */ | ||||||
|  |         @media only screen and (max-width: 620px) { | ||||||
|  |         table[class="body"] h1 { | ||||||
|  |         font-size: 28px !important; | ||||||
|  |         margin-bottom: 10px !important; | ||||||
|  |         } | ||||||
|  |         table[class="body"] p, | ||||||
|  |         table[class="body"] ul, | ||||||
|  |         table[class="body"] ol, | ||||||
|  |         table[class="body"] td, | ||||||
|  |         table[class="body"] span, | ||||||
|  |         table[class="body"] a { | ||||||
|  |         font-size: 16px !important; | ||||||
|  |         } | ||||||
|  |         table[class="body"] .wrapper, | ||||||
|  |         table[class="body"] .article { | ||||||
|  |         padding: 10px !important; | ||||||
|  |         } | ||||||
|  |         table[class="body"] .content { | ||||||
|  |         padding: 0 !important; | ||||||
|  |         } | ||||||
|  |         table[class="body"] .container { | ||||||
|  |         padding: 0 !important; | ||||||
|  |         width: 100% !important; | ||||||
|  |         } | ||||||
|  |         table[class="body"] .main { | ||||||
|  |         border-left-width: 0 !important; | ||||||
|  |         border-radius: 0 !important; | ||||||
|  |         border-right-width: 0 !important; | ||||||
|  |         } | ||||||
|  |         table[class="body"] .btn table { | ||||||
|  |         width: 100% !important; | ||||||
|  |         } | ||||||
|  |         table[class="body"] .btn a { | ||||||
|  |         width: 100% !important; | ||||||
|  |         } | ||||||
|  |         table[class="body"] .img-responsive { | ||||||
|  |         height: auto !important; | ||||||
|  |         max-width: 100% !important; | ||||||
|  |         width: auto !important; | ||||||
|  |         } | ||||||
|  |         } | ||||||
|  |         /* ------------------------------------- | ||||||
|  |         PRESERVE THESE STYLES IN THE HEAD | ||||||
|  |         ------------------------------------- */ | ||||||
|  |         @media all { | ||||||
|  |         .ExternalClass { | ||||||
|  |         width: 100%; | ||||||
|  |         } | ||||||
|  |         .ExternalClass, | ||||||
|  |         .ExternalClass p, | ||||||
|  |         .ExternalClass span, | ||||||
|  |         .ExternalClass font, | ||||||
|  |         .ExternalClass td, | ||||||
|  |         .ExternalClass div { | ||||||
|  |         line-height: 100%; | ||||||
|  |         } | ||||||
|  |         .apple-link a { | ||||||
|  |         color: inherit !important; | ||||||
|  |         font-family: inherit !important; | ||||||
|  |         font-size: inherit !important; | ||||||
|  |         font-weight: inherit !important; | ||||||
|  |         line-height: inherit !important; | ||||||
|  |         text-decoration: none !important; | ||||||
|  |         } | ||||||
|  |         .btn-primary table td:hover { | ||||||
|  |         /* background-color: rgb(50, 110, 103) !important; */ | ||||||
|  |         } | ||||||
|  |         .btn-primary a:hover { | ||||||
|  |         background-color: rgb(50, 110, 103) !important; | ||||||
|  |         border-color: rgb(50, 110, 103) !important; | ||||||
|  |         } | ||||||
|  |         } | ||||||
|  |         </style> | ||||||
|  |         </head> | ||||||
|  |         <body class=""> | ||||||
|  |         <span class="preheader"></span> | ||||||
|  |         <table d="" role="presentation" border="0" cellpadding="0" cellspacing="0" class="body"> | ||||||
|  |         <tr> | ||||||
|  |         <td> </td> | ||||||
|  |         <td class="container"> | ||||||
|  |         <div class="content"> | ||||||
|  |         <!-- START CENTERED WHITE CONTAINER --> | ||||||
|  |         <table border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width:100%"> | ||||||
|  |         <tbody> | ||||||
|  |         <tr> | ||||||
|  |         <td valign="top" style="padding:0px"> | ||||||
|  |         <table align="left" width="100%" border="0" cellpadding="0" cellspacing="0" style="min-width:100%"> | ||||||
|  |         <tbody> | ||||||
|  |         <tr> | ||||||
|  |         <td valign="top" style="padding-right:0px;padding-left:0px;padding-top:0;padding-bottom:0;text-align:center"> | ||||||
|  |         <img align="center" alt="Telecom Infra Project" src="https://gallery.mailchimp.com/d068b19c9ce4cd81f132f1844/images/f4ac7418-c41c-4aee-890b-efe01f3302cb.png" | ||||||
|  |              width="600" style="max-width:100%;padding-bottom:0;display:inline!important;vertical-align:bottom"> | ||||||
|  |         </td> | ||||||
|  |         </tr> | ||||||
|  |         </tbody> | ||||||
|  |         </table> | ||||||
|  |         </td> | ||||||
|  |         </tr> | ||||||
|  |         </tbody> | ||||||
|  |         </table> | ||||||
|  |         <table role="presentation" class="main"> | ||||||
|  |         <!-- START MAIN CONTENT AREA --> | ||||||
|  |         <tr> | ||||||
|  |         <td class="wrapper"> | ||||||
|  |         <table role="presentation" border="0" cellpadding="0" cellspacing="0"> | ||||||
|  |         <tr> | ||||||
|  |         <td> | ||||||
|  |         <h2>Project Groups now available to join - Telecom Infra Project</h2> | ||||||
|  |         <p>Dear Jaspreet Sachdev,</p> | ||||||
|  |         <p> | ||||||
|  |         Facebook is now a member of the following TIP Project Group, | ||||||
|  |         which means they are available for you to join: | ||||||
|  |         </p> | ||||||
|  |         <p> | ||||||
|  |         <ul style="text-align: left;margin: auto;display: inline-block;"> | ||||||
|  |         <li><a href="https://urldefense.com/" target="_blank">Wi-Fi</a></li> | ||||||
|  |         </ul> | ||||||
|  |         </p> | ||||||
|  |         <p> | ||||||
|  |         If you would like to participate in any of these groups, you may join | ||||||
|  |         by logging in to your TIP Membership Account and navigating to My Project | ||||||
|  |         Groups. | ||||||
|  |         </p> | ||||||
|  |         </td> | ||||||
|  |         </tr> | ||||||
|  |         <tr> | ||||||
|  |         <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary"> | ||||||
|  |         <tbody> | ||||||
|  |         <tr> | ||||||
|  |         <td> | ||||||
|  |         <table role="presentation" border="0" cellpadding="0" cellspacing="0"> | ||||||
|  |         <tbody> | ||||||
|  |         <tr> | ||||||
|  |         <td> | ||||||
|  |         <a href="https://urldefense.com/v3/" target="_blank">Account Login</a> | ||||||
|  |         </td> | ||||||
|  |         </tr> | ||||||
|  |         </tbody> | ||||||
|  |         </table> | ||||||
|  |         </td> | ||||||
|  |         </tr> | ||||||
|  |         </tbody> | ||||||
|  |         </table> | ||||||
|  |         </tr> | ||||||
|  |         <tr> | ||||||
|  |         <td> | ||||||
|  |         <p>Questions? <a href="https://urldefense.com/v3/" target="_blank">Contact TIP Support</a> or email support@telecominfraproject.com.</p> | ||||||
|  |         </td> | ||||||
|  |         </tr> | ||||||
|  |         <tr> | ||||||
|  |         <td> | ||||||
|  |         <p>Sincerely,</p> | ||||||
|  |         <p>Telecom Infra Project</p> | ||||||
|  |         </td> | ||||||
|  |         </tr> | ||||||
|  |         </table> | ||||||
|  |         </td> | ||||||
|  |         </tr> | ||||||
|  |         <!-- END MAIN CONTENT AREA --> | ||||||
|  |         </table> | ||||||
|  |         =09 | ||||||
|  |         <!-- END CENTERED WHITE CONTAINER --> | ||||||
|  |         <div class="footer"> | ||||||
|  |         <table border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width:100;margin-top:30px;"> | ||||||
|  |         <tbody> | ||||||
|  |         <tr> | ||||||
|  |         <td valign="top" style="padding:0px"> | ||||||
|  |         <table align="left" width="100%" border="0" cellpadding="0" cellspacing="0" style="min-width:100%"> | ||||||
|  |         <tbody> | ||||||
|  |         <tr> | ||||||
|  |         <tr> | ||||||
|  |         <p style="text-align:center"> | ||||||
|  |         Copyright =C2=A9 2021 Telecom Infra Project, All rights reserved. | ||||||
|  |         </p> | ||||||
|  |         <a href="https://urldefense.com/v3/" target="_blank">www.telecominfraproject.com</a> | ||||||
|  |         </tr> | ||||||
|  |         </tr> | ||||||
|  |         </tbody> | ||||||
|  |         </table> | ||||||
|  |         </td> | ||||||
|  |         </tr> | ||||||
|  |         </tbody> | ||||||
|  |         </table> | ||||||
|  |         </div> | ||||||
|  |         </div> | ||||||
|  |         </td> | ||||||
|  |         <td> </td> | ||||||
|  |         </tr> | ||||||
|  |         </table> | ||||||
|  |         <img src="https://mandrillapp.com/track/open.php?u=31039844&id=6424f96bfb88484685c7229756fee3fb" height="1" width="1"></body> | ||||||
|  |         </html> | ||||||
							
								
								
									
										0
									
								
								templates/sub_email_verification.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								templates/sub_email_verification.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								templates/sub_email_verification.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								templates/sub_email_verification.txt
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								templates/sub_password_reset.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								templates/sub_password_reset.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								templates/sub_password_reset.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								templates/sub_password_reset.txt
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								templates/sub_verification_code.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								templates/sub_verification_code.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								templates/sub_verification_code.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								templates/sub_verification_code.txt
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								templates/verification_code.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								templates/verification_code.html
									
									
									
									
									
										Normal file
									
								
							| @@ -96,7 +96,7 @@ | |||||||
|  |  | ||||||
| <div class="logo-grid"> | <div class="logo-grid"> | ||||||
|     <div></div> |     <div></div> | ||||||
|     <div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div> |     <div><img src="/wwwassets/logo.png" alt="OpenWifi"></div> | ||||||
|     <div></div> |     <div></div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -103,7 +103,7 @@ | |||||||
|  |  | ||||||
| <div class="logo-grid"> | <div class="logo-grid"> | ||||||
|     <div></div> |     <div></div> | ||||||
|     <div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div> |     <div><img src="/wwwassets/logo.png" alt="OpenWifi"></div> | ||||||
|     <div></div> |     <div></div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -103,7 +103,7 @@ | |||||||
|  |  | ||||||
| <div class="logo-grid"> | <div class="logo-grid"> | ||||||
|   <div></div> |   <div></div> | ||||||
|   <div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div> |   <div><img src="/wwwassets/logo.png" alt="OpenWifi"></div> | ||||||
|   <div></div> |   <div></div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								wwwassets/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								wwwassets/logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.8 KiB | 
| @@ -101,7 +101,7 @@ | |||||||
|  |  | ||||||
| <div class="logo-grid"> | <div class="logo-grid"> | ||||||
|     <div></div> |     <div></div> | ||||||
|     <div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div> |     <div><img src="/wwwassets/logo.png" alt="OpenWifi"></div> | ||||||
|     <div></div> |     <div></div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -122,7 +122,7 @@ | |||||||
|  |  | ||||||
| <div class="logo-grid"> | <div class="logo-grid"> | ||||||
|     <div></div> |     <div></div> | ||||||
|     <div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div> |     <div><img src="/wwwassets/logo.png" alt="OpenWifi"></div> | ||||||
|     <div></div> |     <div></div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -101,7 +101,7 @@ | |||||||
|  |  | ||||||
| <div class="logo-grid"> | <div class="logo-grid"> | ||||||
|     <div></div> |     <div></div> | ||||||
|     <div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div> |     <div><img src="/wwwassets/logo.png" alt="OpenWifi"></div> | ||||||
|     <div></div> |     <div></div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -70,7 +70,7 @@ | |||||||
| <body> | <body> | ||||||
|     <div class="logo-grid"> |     <div class="logo-grid"> | ||||||
|         <div></div> |         <div></div> | ||||||
|         <div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div> |         <div><img src="/wwwassets/logo.png" alt="OpenWifi"></div> | ||||||
|         <div></div> |         <div></div> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -122,7 +122,7 @@ | |||||||
|  |  | ||||||
| <div class="logo-grid"> | <div class="logo-grid"> | ||||||
|     <div></div> |     <div></div> | ||||||
|     <div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div> |     <div><img src="/wwwassets/sub_logo.png" alt="OpenWifi"></div> | ||||||
|     <div></div> |     <div></div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -101,7 +101,7 @@ | |||||||
|  |  | ||||||
| <div class="logo-grid"> | <div class="logo-grid"> | ||||||
|     <div></div> |     <div></div> | ||||||
|     <div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div> |     <div><img src="/wwwassets/sub_logo.png" alt="OpenWifi"></div> | ||||||
|     <div></div> |     <div></div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -69,7 +69,7 @@ | |||||||
| <body> | <body> | ||||||
| <div class="logo-grid"> | <div class="logo-grid"> | ||||||
|     <div></div> |     <div></div> | ||||||
|     <div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div> |     <div><img src="/wwwassets/sub_logo.png" alt="OpenWifi"></div> | ||||||
|     <div></div> |     <div></div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								wwwassets/sub_404_error.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								wwwassets/sub_404_error.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								wwwassets/sub_404_error.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								wwwassets/sub_404_error.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								wwwassets/sub_access_policy.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								wwwassets/sub_access_policy.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								wwwassets/sub_email_verification_error.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								wwwassets/sub_email_verification_error.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								wwwassets/sub_email_verification_success.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								wwwassets/sub_email_verification_success.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										
											BIN
										
									
								
								wwwassets/sub_favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								wwwassets/sub_favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 202 KiB | 
							
								
								
									
										
											BIN
										
									
								
								wwwassets/sub_logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								wwwassets/sub_logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.8 KiB | 
							
								
								
									
										0
									
								
								wwwassets/sub_password_policy.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								wwwassets/sub_password_policy.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								wwwassets/sub_password_reset.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								wwwassets/sub_password_reset.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								wwwassets/sub_password_reset_error.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								wwwassets/sub_password_reset_error.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								wwwassets/sub_password_reset_success.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								wwwassets/sub_password_reset_success.html
									
									
									
									
									
										Normal file
									
								
							
		Reference in New Issue
	
	Block a user
	 stephb9959
					stephb9959