app/controllers/AccountController.php (449 lines of code) (raw):

<?php class AccountController extends BaseController { const PASSWORD_VALIDATION = "required|min:6|max:48|regex:/^.*(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[@!$#*&]).*$/"; const PASSWORD_VALIDATION_MESSAGE = "Password needs to contain at least (a) One lower case letter (b) One Upper case letter and (c) One number (d) One of the following special characters - !@#$&*"; public function __construct() { Session::put("nav-active", "user-dashboard"); } public function createAccountView() { $auth_options = Config::get('pga_config.wsis')['auth-options']; $auth_password_option = CommonUtilities::getAuthPasswordOption(); if ($auth_password_option == null) { return Redirect::to("login"); } $auth_code_options = CommonUtilities::getAuthCodeOptions(); return View::make('account/create', array( "auth_password_option" => $auth_password_option, "auth_code_options" => $auth_code_options)); } public function createAccountSubmit() { $rules = array( "username" => "required|min:6|regex:/^[a-z0-9_-]+$/", "password" => self::PASSWORD_VALIDATION, "confirm_password" => "required|same:password", "email" => "required|email", "confirm_email" => "required|same:email", ); $messages = array( 'username.regex' => "Username can only contain lowercase letters, numbers, underscores and hyphens.", 'password.regex' => self::PASSWORD_VALIDATION_MESSAGE, ); $validator = Validator::make(Input::all(), $rules, $messages); if ($validator->fails()) { return Redirect::to("create") ->withInput(Input::except('password', 'confirm_password', 'email', 'confirm_email')) ->withErrors($validator); } $first_name = $_POST['first_name']; $last_name = $_POST['last_name']; $username = $_POST['username']; $password = $_POST['password']; $email = $_POST['email']; if (Keycloak::usernameExists($username)) { return Redirect::to("create") ->withInput(Input::except('password', 'confirm_password')) ->with("username_exists", true); } else { IamAdminServicesUtilities::registerUser($username, $email, $first_name, $last_name, $password); // add user to initial role IamAdminServicesUtilities::addInitialRoleToUser($username); // Send account confirmation email EmailUtilities::sendVerifyEmailAccount($username, $first_name, $last_name, $email); CommonUtilities::print_success_message('Account confirmation request was sent to your email account'); return View::make('home'); } } public function loginView() { // If user already logged in, redirect to home if(CommonUtilities::id_in_session()){ return Redirect::to("home"); } // Only support for one password option $auth_password_option = CommonUtilities::getAuthPasswordOption(); // Support for many external identity providers (authorization code auth flow) $auth_code_options = CommonUtilities::getAuthCodeOptions(); $has_auth_code_and_password_options = $auth_password_option != null && count($auth_code_options) > 0; // If no username/password option and only one external identity // provider, just redirect immediately if ($auth_password_option == null && count($auth_code_options) == 1) { return Redirect::away($auth_code_options[0]["auth_url"]); } else { return View::make('account/login', array( "auth_password_option" => $auth_password_option, "auth_code_options" => $auth_code_options, "has_auth_code_and_password_options" => $has_auth_code_and_password_options, )); } } public function loginDesktopView() { // Only support for one password option $auth_password_option = CommonUtilities::getAuthPasswordOption(); // Support for many external identity providers (authorization code auth flow) $auth_code_options = CommonUtilities::getAuthCodeOptions(); $has_auth_code_and_password_options = $auth_password_option != null && count($auth_code_options) > 0; // If no username/password option and only one external identity // provider, just redirect immediately if ($auth_password_option == null && count($auth_code_options) == 1) { return Redirect::away($auth_code_options[0]["auth_url"]); } else { return View::make('account/login-desktop', array( "auth_password_option" => $auth_password_option, "auth_code_options" => $auth_code_options, "has_auth_code_and_password_options" => $has_auth_code_and_password_options, )); } } public function loginSubmit() { if (CommonUtilities::form_submitted()) { $username = strtolower(Input::get("username")); $password = $_POST['password']; $response = Keycloak::authenticate($username, $password); if(!isset($response->access_token)){ if (Keycloak::isUpdatePasswordRequired($username)) { return Redirect::to("login" . '?status=failed')->with("update-password-required", true); } else { return Redirect::to("login". '?status=failed')->with("invalid-credentials", true); } } $accessToken = $response->access_token; $refreshToken = $response->refresh_token; $expirationTime = time() + $response->expires_in - 300; // 5 minutes safe margin $userProfile = Keycloak::getUserProfileFromOAuthToken($accessToken); Session::put("iam-user-profile", $userProfile); $username = $userProfile['username']; $userRoles = $userProfile['roles']; $userEmail = $userProfile["email"]; $firstName = $userProfile["firstname"]; $lastName = $userProfile["lastname"]; $authzToken = new Airavata\Model\Security\AuthzToken(); $authzToken->accessToken = $accessToken; $authzToken->claimsMap['gatewayID'] = Config::get('pga_config.airavata')['gateway-id']; $authzToken->claimsMap['userName'] = $username; Session::put('authz-token',$authzToken); Session::put('oauth-refresh-code',$refreshToken); Session::put('oauth-expiration-time',$expirationTime); Session::put("roles", $userRoles); // AIRAVATA-3086: get gateway groups and get the groups this user is a member of $gatewayGroups = Airavata::getGatewayGroups($authzToken); $groupMemberships = GroupManagerService::getAllGroupsUserBelongs( $authzToken, $username . "@" . Config::get('pga_config.airavata')['gateway-id']); $get_group_id = function($group) { return $group->id; }; $userGroupIds = array_map($get_group_id, $groupMemberships); // AIRAVATA-3086: check if user is in Admins group if (in_array($gatewayGroups->adminsGroupId, $userGroupIds)) { Session::put("admin", true); } // AIRAVATA-3086: check if user is in Read Only Admins group if (in_array($gatewayGroups->readOnlyAdminsGroupId, $userGroupIds)) { Session::put("authorized-user", true); Session::put("admin-read-only", true); } // AIRAVATA-3086: check if user is in default Gateway Users group if (in_array($gatewayGroups->defaultGatewayUsersGroupId, $userGroupIds)) { Session::put("authorized-user", true); } // AIRAVATA-3086: leave this for scigap/super-admin portal //gateway-provider-code if (in_array("gateway-provider", $userRoles)) { Session::put("gateway-provider", true); } // AIRAVATA-3086: for scigap/super-admin portal, keep same role-based rules //only for super admin if( Config::get('pga_config.portal')['super-admin-portal'] == true && in_array(Config::get('pga_config.wsis')['admin-role-name'], $userRoles)) { Session::put("super-admin", true); } CommonUtilities::store_id_in_session($username); Session::put("gateway_id", Config::get('pga_config.airavata')['gateway-id']); UserProfileUtilities::initialize_user_profile(); if(Session::has("admin") || Session::has("admin-read-only") || Session::has("authorized-user") || Session::has("gateway-provider")){ return $this->initializeWithAiravata($username, $userEmail, $firstName, $lastName, $accessToken, $refreshToken, $expirationTime); } return Redirect::to("account/dashboard" . "?status=less_privileged&code=".$accessToken . "&username=".$username . "&refresh_code=" . $refreshToken . "&valid_time=" . $expirationTime); } } public function apiLoginSubmit() { $username = strtolower(Input::get("username")); $password = Input::get("password"); $response = Keycloak::authenticate($username, $password); return Response::json($response); } public function oauthCallback() { if (!isset($_GET["code"])) { return Redirect::to('home'); } $code = $_GET["code"]; $response = Keycloak::getOAuthToken($code); if(!isset($response->access_token)){ return Redirect::to('home'); } $accessToken = $response->access_token; $refreshToken = $response->refresh_token; $expirationTime = time() + $response->expires_in - 300; //5 minutes safe margin $userProfile = Keycloak::getUserProfileFromOAuthToken($accessToken); Log::debug("userProfile", array($userProfile)); Session::put("iam-user-profile", $userProfile); $username = $userProfile['username']; $userRoles = $userProfile['roles']; $userEmail = $userProfile['email']; $firstName = $userProfile['firstname']; $lastName = $userProfile['lastname']; # As a workaround to figuring out if the user is logging in for the first # time, if the user has no roles, assume they are logging in for the first # time and add them to the initial role if (!$this->hasAnyRoles($userRoles)){ IamAdminServicesUtilities::addInitialRoleToUser($username); # Reload the roles $userProfile = Keycloak::getUserProfileFromOAuthToken($accessToken); $userRoles = $userProfile['roles']; # Notify admin $this->sendAccountCreationNotification2Admin($username); } $authzToken = new Airavata\Model\Security\AuthzToken(); $authzToken->accessToken = $accessToken; $authzToken->claimsMap = array('userName'=>$username, 'gatewayID'=> Config::get('pga_config.airavata')['gateway-id']); Session::put('authz-token',$authzToken); Session::put('oauth-refresh-code',$refreshToken); Session::put('oauth-expiration-time',$expirationTime); Session::put("roles", $userRoles); // AIRAVATA-3086: get gateway groups and get the groups this user is a member of $gatewayGroups = Airavata::getGatewayGroups($authzToken); $groupMemberships = GroupManagerService::getAllGroupsUserBelongs( $authzToken, $username . "@" . Config::get('pga_config.airavata')['gateway-id']); $get_group_id = function($group) { return $group->id; }; $userGroupIds = array_map($get_group_id, $groupMemberships); // AIRAVATA-3086: check if user is in Admins group if (in_array($gatewayGroups->adminsGroupId, $userGroupIds)) { Session::put("admin", true); } // AIRAVATA-3086: check if user is in Read Only Admins group if (in_array($gatewayGroups->readOnlyAdminsGroupId, $userGroupIds)) { Session::put("authorized-user", true); Session::put("admin-read-only", true); } // AIRAVATA-3086: check if user is in default Gateway Users group if (in_array($gatewayGroups->defaultGatewayUsersGroupId, $userGroupIds)) { Session::put("authorized-user", true); } // AIRAVATA-3086: leave this for scigap/super-admin portal //gateway-provider-code if (in_array("gateway-provider", $userRoles)) { Session::put("gateway-provider", true); } // AIRAVATA-3086: for scigap/super-admin portal, keep same role-based rules //only for super admin if( Config::get('pga_config.portal')['super-admin-portal'] == true && in_array(Config::get('pga_config.wsis')['admin-role-name'], $userRoles)) { Session::put("super-admin", true); } CommonUtilities::store_id_in_session($username); Session::put("gateway_id", Config::get('pga_config.airavata')['gateway-id']); UserProfileUtilities::initialize_user_profile(); if(Session::has("admin") || Session::has("admin-read-only") || Session::has("authorized-user") || Session::has("gateway-provider")){ return $this->initializeWithAiravata($username, $userEmail, $firstName, $lastName, $accessToken, $refreshToken, $expirationTime); } return Redirect::to("account/dashboard" . "?status=less_privileged&code=".$accessToken . "&username=".$username . "&refresh_code=" . $refreshToken . "&valid_time=" . $expirationTime); } private function hasAnyRoles($roles) { return in_array("gateway-provider", $roles) or in_array("user-pending", $roles) or in_array(Config::get('pga_config.wsis')['admin-role-name'], $roles) or in_array(Config::get('pga_config.wsis')['read-only-admin-role-name'], $roles) or in_array(Config::get('pga_config.wsis')['user-role-name'], $roles) or in_array(Config::get('pga_config.wsis')['initial-role-name'], $roles); } private function initializeWithAiravata($username, $userEmail, $firstName, $lastName, $accessToken, $refreshToken, $validTime){ // Log the user out if Airavata is down. If a new user we want to make // sure we create the default project and setup experiment storage // before they do anything else. if (!CommonUtilities::isAiravataUp()) { Session::flush(); return Redirect::to("home")->with("airavata-down", true); } $userProfile = UserProfileUtilities::get_user_profile($username); Session::put('user-profile', $userProfile); //creating a default project for user $projects = ProjectUtilities::get_all_user_projects(Config::get('pga_config.airavata')['gateway-id'], $username); if($projects == null || count($projects) == 0){ //creating a default project for user ProjectUtilities::create_default_project($username); } $dirPath = Config::get('pga_config.airavata')['experiment-data-absolute-path'] . "/" . Session::get('username'); if(!file_exists($dirPath)){ $old_umask = umask(0); mkdir($dirPath, 0777, true); umask($old_umask); } // Must create the UserResourceProfile before we can add auto provisioned accounts to it $user_resource_profile = URPUtilities::get_or_create_user_resource_profile(); $auto_provisioned_accounts = URPUtilities::setup_auto_provisioned_accounts(); // Log::debug("auto_provisioned_accounts", array($auto_provisioned_accounts)); if(Session::has("admin") || Session::has("admin-read-only") || Session::has("gateway-provider")){ $response = Redirect::to("admin/dashboard". "?status=ok&code=".$accessToken . "&username=".$username . "&refresh_code=" . $refreshToken . "&valid_time=" . $validTime); if (!empty($auto_provisioned_accounts)) { $response = $response->with("auto_provisioned_accounts", $auto_provisioned_accounts); } return $response; }else{ $response = Redirect::to("account/dashboard". "?status=ok&code=".$accessToken ."&username=".$username . "&refresh_code=" . $refreshToken . "&valid_time=" . $validTime); if (!empty($auto_provisioned_accounts)) { $response = $response->with("auto_provisioned_accounts", $auto_provisioned_accounts); } return $response; } } public function forgotPassword() { // $capatcha = WSIS::getCapatcha()->return; return View::make("account/forgot-password"); } public function forgotPasswordSubmit() { $username = strtolower(Input::get("username")); if(empty($username)){ CommonUtilities::print_error_message("Please provide a valid username"); return View::make("account/forgot-password"); }else{ try{ $user_profile = Keycloak::getUserProfile($username); EmailUtilities::sendPasswordResetEmail($username, $user_profile["firstname"], $user_profile["lastname"], $user_profile["email"]); return Redirect::to("forgot-password")->with("forgot-password-success", "Password reset notification was sent to your email account"); }catch (Exception $ex){ Log::error($ex); return Redirect::to("forgot-password")->with("forgot-password-error", "Password reset operation failed"); } } } public function dashboard(){ // dashboard requires that the user is logged in, if not redirect to login if (!CommonUtilities::id_in_session()) { return Redirect::to("login"); } if (Session::has("user-profile")) { $userEmail = Session::get("user-profile")->emails[0]; } else { $userEmail = Session::get("iam-user-profile")["email"]; } return View::make("account/dashboard"); } public function resetPassword() { $code = Input::get("code", Input::old("code")); $username = Input::get("username", Input::old("username")); if(empty($username) || empty($code)){ return Redirect::to("forgot-password")->with("password-reset-error", "Reset password link failed. Please request to reset user password again."); }else{ return View::make("account/reset-password", array("code" => $code, "username"=>$username, "password_regex_tooltip"=>self::PASSWORD_VALIDATION_MESSAGE)); } } public function confirmAccountCreation() { $code = Input::get("code"); $username = Input::get("username"); if(empty($username) || empty($code)){ return View::make("home"); }else{ try{ $enabled = IamAdminServicesUtilities::isUserEnabled($username); if ($enabled) { return Redirect::to("login")->with("account-created-success", "Your account has already been successfully created. Please log in now."); } else { $verified = EmailUtilities::verifyEmailVerification($username, $code); if (!$verified){ $user_profile = Keycloak::getUserProfile($username); EmailUtilities::sendVerifyEmailAccount($username, $user_profile["firstname"], $user_profile["lastname"], $user_profile["email"]); CommonUtilities::print_error_message("Account confirmation " . "failed! We're sending another confirmation email. " . "Please click the link in the confirmation email that " . "you should be receiving soon."); return View::make("home"); } $result = IamAdminServicesUtilities::enableUser($username); if($result){ $this->sendAccountCreationNotification2Admin($username); return Redirect::to("login")->with("account-created-success", "Your account has been successfully created. Please log in now."); }else{ CommonUtilities::print_error_message("Account confirmation failed! Please contact the Gateway Admin"); return View::make("home"); } } }catch (Exception $e){ CommonUtilities::print_error_message("Account confirmation failed! Please contact the Gateway Admin"); return View::make("home"); } } } private function sendAccountCreationNotification2Admin($username){ $mail = new PHPMailer; $mail->isSMTP(); // Note: setting SMTPDebug will cause output to be dumped into the // response, so only enable for testing purposes // $mail->SMTPDebug = 3; $mail->Host = Config::get('pga_config.portal')['portal-smtp-server-host']; $mail->SMTPAuth = true; $mail->Username = Config::get('pga_config.portal')['portal-email-username']; $mail->Password = Config::get('pga_config.portal')['portal-email-password']; $mail->SMTPSecure = "tls"; $mail->Port = intval(Config::get('pga_config.portal')['portal-smtp-server-port']); $mail->From = Config::get('pga_config.portal')['portal-email-username']; $gatewayURL = $_SERVER['SERVER_NAME']; $portalTitle = Config::get('pga_config.portal')['portal-title']; $mail->FromName = "$portalTitle ($gatewayURL)"; $recipients = Config::get('pga_config.portal')['admin-emails']; foreach($recipients as $recipient){ $mail->addAddress($recipient); } $mail->isHTML(true); $mail->Subject = "New User Account Was Created Successfully"; $userProfile = Keycloak::getUserProfile($username); $wsisConfig = Config::get('pga_config.wsis'); $tenant = $wsisConfig['tenant-domain']; $str = "Gateway Portal: " . $_SERVER['SERVER_NAME'] ."<br/>"; $str = $str . "Tenant: " . $tenant . "<br/>"; $str = $str . "Username: " . $username ."<br/>"; $str = $str . "Name: " . $userProfile["firstname"] . " " . $userProfile["lastname"] . "<br/>"; $str = $str . "Email: " . $userProfile["email"]; $mail->Body = $str; $mail->send(); } public function resetPasswordSubmit() { $rules = array( "new_password" => self::PASSWORD_VALIDATION, "confirm_new_password" => "required|same:new_password", ); $messages = array( 'new_password.regex' => self::PASSWORD_VALIDATION_MESSAGE, ); $validator = Validator::make(Input::all(), $rules, $messages); if ($validator->fails()) { Log::debug("validation failed", array($validator->messages())); return Redirect::to("reset-password") ->withInput(Input::except('new_password', 'confirm_new_password')) ->withErrors($validator); } $code = $_POST['code']; $username = $_POST['username']; $new_password = $_POST['new_password']; try{ $verified = EmailUtilities::verifyPasswordResetCode($username, $code); if (!$verified){ return Redirect::to("forgot-password")->with("password-reset-error", "Resetting user password operation failed. Please request to reset user password again."); } $result = IamAdminServicesUtilities::resetUserPassword($username, $new_password); if($result){ return Redirect::to("login")->with("password-reset-success", "User password was reset successfully"); }else{ Log::error("Failed to reset password for user $username"); return Redirect::to("reset-password") ->withInput(Input::except('new_password', 'confirm_new_password')) ->with("password-reset-error", "Resetting user password operation failed"); } }catch (Exception $e){ Log::error("Failed to reset password for user $username"); Log::error($e); return Redirect::to("reset-password") ->withInput(Input::except('new_password', 'confirm_new_password')) ->with("password-reset-error", "Resetting user password operation failed"); } } public function getRefreshedTokenForDesktop(){ $refreshToken = Input::get('refresh_code'); $response = Keycloak::getRefreshedOAuthToken($refreshToken); if(isset($response->access_token)){ $accessToken = $response->access_token; $refreshToken = $response->refresh_token; $expirationTime = $response->expires_in; // 5 minutes safe margin echo json_encode(array('status'=>'ok', 'code'=>$accessToken, 'refresh_code'=>$refreshToken, 'valid_time'=>$expirationTime)); }else{ echo json_encode(array('status'=>'failed')); } } public function logout() { Session::flush(); return Redirect::away(Keycloak::getOAuthLogoutUrl(URL::to("/"))); } public function allocationRequestView(){ return View::make("account/request-allocation"); } public function allocationRequestSubmit(){ return 'result'; } public function noticeSeenAck(){ Session::put( "notice-count", Input::get("notice-count")); Session::put("notice-seen", true); } }