Skip to content

Commit

Permalink
client session required actions
Browse files Browse the repository at this point in the history
  • Loading branch information
patriot1burke committed Sep 2, 2015
1 parent 38a7214 commit 7d4b93e
Show file tree
Hide file tree
Showing 15 changed files with 209 additions and 69 deletions.
Expand Up @@ -304,7 +304,9 @@ module.controller('UserTabCtrl', function($scope, $location, Dialog, Notificatio
};
});

module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser, User, UserExecuteActionsEmail, UserFederationInstances, UserImpersonation, RequiredActions, $location, Dialog, Notifications) {
module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser, User,
UserFederationInstances, UserImpersonation, RequiredActions,
$location, Dialog, Notifications) {
$scope.realm = realm;
$scope.create = !user.id;
$scope.editUsername = $scope.create || $scope.realm.editUsernameAllowed;
Expand Down Expand Up @@ -374,36 +376,12 @@ module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser
}

});

/*[
{id: "VERIFY_EMAIL", text: "Verify Email"},
{id: "UPDATE_PROFILE", text: "Update Profile"},
{id: "CONFIGURE_TOTP", text: "Configure Totp"},
{id: "UPDATE_PASSWORD", text: "Update Password"}
];
*/

$scope.$watch('user', function() {
if (!angular.equals($scope.user, user)) {
$scope.changed = true;
}
}, true);

$scope.sendExecuteActionsEmail = function() {
if ($scope.changed) {
Dialog.message("Cannot send email", "You must save your current changes before you can send an email");
return;
}
Dialog.confirm('Send Email', 'Are you sure you want to send email to user?', function() {
UserExecuteActionsEmail.update({ realm: realm.realm, userId: user.id }, { }, function() {
Notifications.success("Email sent to user");
}, function() {
Notifications.error("Failed to send email to user");
});
});
};


$scope.save = function() {
convertAttributeValuesToLists();

Expand Down Expand Up @@ -476,7 +454,7 @@ module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser
}
});

module.controller('UserCredentialsCtrl', function($scope, realm, user, User, UserCredentials, Notifications, Dialog) {
module.controller('UserCredentialsCtrl', function($scope, realm, user, RequiredActions, User, UserExecuteActionsEmail, UserCredentials, Notifications, Dialog) {
console.log('UserCredentialsCtrl');

$scope.realm = realm;
Expand All @@ -487,6 +465,18 @@ module.controller('UserCredentialsCtrl', function($scope, realm, user, User, Use
if(!!user.totp){
$scope.isTotp = user.totp;
}
// ID - Name map for required actions. IDs are enum names.
RequiredActions.query({realm: realm.realm}, function(data) {
$scope.userReqActionList = [];
for (var i = 0; i < data.length; i++) {
console.log("listed required action: " + data[i].name);
if (data[i].enabled) {
var item = data[i];
$scope.userReqActionList.push(item);
}
}

});

$scope.resetPassword = function() {
if ($scope.pwdChange) {
Expand Down Expand Up @@ -528,6 +518,24 @@ module.controller('UserCredentialsCtrl', function($scope, realm, user, User, Use
});
};

$scope.emailActions = [];

$scope.sendExecuteActionsEmail = function() {
if ($scope.changed) {
Dialog.message("Cannot send email", "You must save your current changes before you can send an email");
return;
}
Dialog.confirm('Send Email', 'Are you sure you want to send email to user?', function() {
UserExecuteActionsEmail.update({ realm: realm.realm, userId: user.id }, $scope.emailActions, function() {
Notifications.success("Email sent to user");
$scope.emailActions = [];
}, function() {
Notifications.error("Failed to send email to user");
});
});
};



$scope.$watch('user', function() {
if (!angular.equals($scope.user, user)) {
Expand Down
20 changes: 20 additions & 0 deletions ...ommon-themes/src/main/resources/theme/base/admin/resources/partials/user-credentials.html 100644 → 100755
Expand Up @@ -44,6 +44,26 @@
<button class="btn btn-danger" type="submit" data-ng-click="removeTotp()" tooltip-trigger="mouseover mouseout" tooltip="Remove one time password generator for user." tooltip-placement="right">Remove TOTP</button>
</div>
</div>
</fieldset >
<fieldset class="border-top" data-ng-show="user.email">
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="reqActions">Reset Actions</label>

<div class="col-md-6">
<select ui-select2 id="reqActions" ng-model="emailActions" data-placeholder="Select an action..." multiple>
<option ng-repeat="action in userReqActionList" value="{{action.alias}}">{{action.name}}</option>
</select>
</div>
<kc-tooltip>Set of actions to execute when sending the user a Reset Actions Email. 'Verify email' sends an email to the user to verify their email address. 'Update profile' requires user to enter in new personal information. 'Update password' requires user to enter in a new password. 'Configure TOTP' requires setup of a mobile password generator.</kc-tooltip>
</div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="reqActionsEmail">Reset Actions Email</label>

<div class="col-md-6">
<button id="reqActionsEmail" class="btn btn-default" data-ng-click="sendExecuteActionsEmail()">Send Email</button>
</div>
<kc-tooltip>Sends an email to user with an embedded link. Clicking on link will allow the user to execute the reset actions. They will not have to login prior to this. For example, set the action to update password, click this button, and the user will be able to change their password without logging in.</kc-tooltip>
</div>
</fieldset>
</form>
</div>
Expand Down
Expand Up @@ -99,14 +99,6 @@
</div>
<kc-tooltip>Require an action when the user logs in. 'Verify email' sends an email to the user to verify their email address. 'Update profile' requires user to enter in new personal information. 'Update password' requires user to enter in a new password. 'Configure TOTP' requires setup of a mobile password generator.</kc-tooltip>
</div>
<div class="form-group clearfix" data-ng-show="!create && user.email">
<label class="col-md-2 control-label" for="reqActionsEmail">Required Actions Email</label>

<div class="col-md-6">
<button id="reqActionsEmail" class="btn btn-default" data-ng-click="sendExecuteActionsEmail()">Send Email</button>
</div>
<kc-tooltip>Sends an email to user with an embedded link. Clicking on link will allow the user to execute all their required actions. They will not have to login prior to this. For example, set the required action to update password, click this button, and the user will be able to change their password without logging in.</kc-tooltip>
</div>

<div class="form-group clearfix" data-ng-if="realm.internationalizationEnabled">
<label class="col-md-2 control-label" for="locale">Locale</label>
Expand Down
Expand Up @@ -48,11 +48,11 @@ public interface UserResource {

@PUT
@Path("execute-actions-email")
public void executeActionsEmail();
public void executeActionsEmail(List<String> actions);

@PUT
@Path("execute-actions-email")
public void executeActionsEmail(@QueryParam("client_id") String clientId);
public void executeActionsEmail(@QueryParam("client_id") String clientId, List<String> actions);

@PUT
@Path("send-verify-email")
Expand Down
Expand Up @@ -53,6 +53,22 @@ public interface ClientSessionModel {
public void removeNote(String name);
public Map<String, String> getNotes();

/**
* Required actions that are attached to this client session.
*
* @return
*/
Set<String> getRequiredActions();

void addRequiredAction(String action);

void removeRequiredAction(String action);

void addRequiredAction(UserModel.RequiredAction action);

void removeRequiredAction(UserModel.RequiredAction action);


/**
* These are notes you want applied to the UserSessionModel when the client session is attached to it.
*
Expand Down
Expand Up @@ -12,6 +12,7 @@

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -200,6 +201,38 @@ public void clearUserSessionNotes() {

}

@Override
public Set<String> getRequiredActions() {
Set<String> copy = new HashSet<>();
copy.addAll(entity.getRequiredActions());
return copy;
}

@Override
public void addRequiredAction(String action) {
entity.getRequiredActions().add(action);
update();

}

@Override
public void removeRequiredAction(String action) {
entity.getRequiredActions().remove(action);
update();

}

@Override
public void addRequiredAction(UserModel.RequiredAction action) {
addRequiredAction(action.name());

}

@Override
public void removeRequiredAction(UserModel.RequiredAction action) {
removeRequiredAction(action.name());
}

void update() {
provider.getTx().replace(cache, entity.getId(), entity);
}
Expand Down
Expand Up @@ -11,6 +11,7 @@

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -197,4 +198,35 @@ public void setAuthenticatedUser(UserModel user) {
else entity.setAuthUserId(user.getId());

}

@Override
public Set<String> getRequiredActions() {
Set<String> copy = new HashSet<>();
copy.addAll(entity.getRequiredActions());
return copy;
}

@Override
public void addRequiredAction(String action) {
entity.getRequiredActions().add(action);

}

@Override
public void removeRequiredAction(String action) {
entity.getRequiredActions().remove(action);

}

@Override
public void addRequiredAction(UserModel.RequiredAction action) {
addRequiredAction(action.name());

}

@Override
public void removeRequiredAction(UserModel.RequiredAction action) {
removeRequiredAction(action.name());
}

}
Expand Up @@ -3,6 +3,7 @@
import org.keycloak.models.ClientSessionModel;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

Expand All @@ -28,6 +29,8 @@ public class ClientSessionEntity {
private Set<String> protocolMappers;
private Map<String, String> notes = new HashMap<>();
private Map<String, String> userSessionNotes = new HashMap<>();
private Set<String> requiredActions = new HashSet<>();


public String getId() {
return id;
Expand Down Expand Up @@ -133,6 +136,10 @@ public Map<String, String> getUserSessionNotes() {
return userSessionNotes;
}

public Set<String> getRequiredActions() {
return requiredActions;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Expand Up @@ -3,6 +3,9 @@
import org.keycloak.models.ClientSessionModel;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -31,6 +34,8 @@ public class ClientSessionEntity extends SessionEntity {
private Map<String, String> userSessionNotes;
private Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus = new HashMap<>();
private String authUserId;
private Set<String> requiredActions = new HashSet<>();


public String getClient() {
return client;
Expand Down Expand Up @@ -136,5 +141,7 @@ public void setUserSessionNotes(Map<String, String> userSessionNotes) {
this.userSessionNotes = userSessionNotes;
}


public Set<String> getRequiredActions() {
return requiredActions;
}
}
Expand Up @@ -19,7 +19,7 @@ public void authenticate(AuthenticationFlowContext context) {
if (context.getExecution().isRequired() ||
(context.getExecution().isOptional() &&
configuredFor(context))) {
context.getUser().addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
context.getClientSession().addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
}
context.success();
}
Expand Down
Expand Up @@ -27,7 +27,7 @@ public void authenticate(AuthenticationFlowContext context) {
if (context.getExecution().isRequired() ||
(context.getExecution().isOptional() &&
configuredFor(context))) {
context.getUser().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
context.getClientSession().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
}
context.success();
}
Expand Down

0 comments on commit 7d4b93e

Please sign in to comment.