blob: 4999aa2e24a0edceaba805e4953b239ef580d1b4 [file] [log] [blame]
<?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;
$authzToken->claimsMap['custosId'] = Config::get('pga_config.wsis')['oauth-client-key'];
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);
$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);
}
}