Skip to content

Commit

Permalink
#62 - configure the user role
Browse files Browse the repository at this point in the history
  • Loading branch information
cbellone committed Oct 21, 2015
1 parent 0bd6c8c commit b78a844
Show file tree
Hide file tree
Showing 15 changed files with 134 additions and 44 deletions.
3 changes: 2 additions & 1 deletion src/main/java/alfio/config/ConfigurationStatusChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import alfio.manager.system.DataMigrator;
import alfio.manager.user.UserManager;
import alfio.model.system.Configuration;
import alfio.model.user.Role;
import alfio.repository.user.AuthorityRepository;
import alfio.repository.user.UserRepository;
import alfio.util.PasswordGenerator;
Expand Down Expand Up @@ -66,7 +67,7 @@ public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
if (!initCompleted) {
String adminPassword = PasswordGenerator.generateRandomPassword();
userRepository.create(UserManager.ADMIN_USERNAME, passwordEncoder.encode(adminPassword), "The", "Administrator", "admin@localhost", true);
authorityRepository.create(UserManager.ADMIN_USERNAME, AuthorityRepository.ROLE_ADMIN);
authorityRepository.create(UserManager.ADMIN_USERNAME, Role.ADMIN.getRoleName());
log.info("*******************************************************");
log.info(" This is the first time you're running alf.io");
log.info(" here the generated admin credentials:");
Expand Down
26 changes: 16 additions & 10 deletions src/main/java/alfio/controller/api/admin/UsersApiController.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
import alfio.model.modification.OrganizationModification;
import alfio.model.modification.UserModification;
import alfio.model.user.Organization;
import alfio.model.user.Role;
import alfio.model.user.User;
import alfio.model.user.UserWithPassword;
import alfio.util.ImageUtil;
import alfio.util.Json;
import alfio.util.ValidationResult;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.Validate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
Expand All @@ -34,10 +36,7 @@
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.security.Principal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;

import static org.springframework.web.bind.annotation.RequestMethod.*;

Expand All @@ -64,6 +63,11 @@ public String unhandledException(Exception e) {
return e.getMessage();
}

@RequestMapping(value = "/roles", method = GET)
public Collection<Role> getAllRoles(Principal principal) {
return userManager.getAvailableRoles(principal.getName());
}

@RequestMapping(value = "/organizations", method = GET)
@ResponseStatus(HttpStatus.OK)
public List<Organization> getAllOrganizations(Principal principal) {
Expand Down Expand Up @@ -101,19 +105,21 @@ public ValidationResult validateOrganization(@RequestBody OrganizationModificati
@RequestMapping(value = "/users/check", method = POST)
public ValidationResult validateUser(@RequestBody UserModification userModification) {
return userManager.validateUser(userModification.getId(), userModification.getUsername(),
userModification.getOrganizationId(), userModification.getFirstName(),
userModification.getOrganizationId(), userModification.getRole(), userModification.getFirstName(),
userModification.getLastName(), userModification.getEmailAddress());
}

@RequestMapping(value = "/users/edit", method = POST)
public String editUser(@RequestBody UserModification userModification) {
userManager.editUser(userModification.getId(), userModification.getOrganizationId(), userModification.getUsername(), userModification.getFirstName(), userModification.getLastName(), userModification.getEmailAddress());
public String editUser(@RequestBody UserModification userModification, Principal principal) {
userManager.editUser(userModification.getId(), userModification.getOrganizationId(), userModification.getUsername(), userModification.getFirstName(), userModification.getLastName(), userModification.getEmailAddress(), Role.valueOf(userModification.getRole()), principal.getName());
return OK;
}

@RequestMapping(value = "/users/new", method = POST)
public UserWithPassword insertUser(@RequestBody UserModification userModification, HttpSession session) {
UserWithPassword userWithPassword = userManager.insertUser(userModification.getOrganizationId(), userModification.getUsername(), userModification.getFirstName(), userModification.getLastName(), userModification.getEmailAddress());
public UserWithPassword insertUser(@RequestBody UserModification userModification, HttpSession session, Principal principal) {
Role requested = Role.valueOf(userModification.getRole());
Validate.isTrue(userManager.getAvailableRoles(principal.getName()).stream().anyMatch(requested::equals), String.format("Requested role %s is not available for current user", userModification.getRole()));
UserWithPassword userWithPassword = userManager.insertUser(userModification.getOrganizationId(), userModification.getUsername(), userModification.getFirstName(), userModification.getLastName(), userModification.getEmailAddress(), requested);
storePasswordImage(session, userWithPassword);
return userWithPassword;
}
Expand Down Expand Up @@ -148,7 +154,7 @@ public String deleteUser(@PathVariable("id") int userId, Principal principal) {
public UserModification loadUser(@PathVariable("id") int userId) {
User user = userManager.findUser(userId);
List<Organization> userOrganizations = userManager.findUserOrganizations(user);
return new UserModification(user.getId(), userOrganizations.get(0).getId(), user.getUsername(), user.getFirstName(), user.getLastName(), user.getEmailAddress());
return new UserModification(user.getId(), userOrganizations.get(0).getId(), userManager.getUserRole(user).name(), user.getUsername(), user.getFirstName(), user.getLastName(), user.getEmailAddress());
}

@RequestMapping(value = "/users/{id}/reset-password", method = PUT)
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/alfio/manager/TicketReservationManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import alfio.model.system.Configuration;
import alfio.model.transaction.PaymentProxy;
import alfio.model.user.Organization;
import alfio.model.user.Role;
import alfio.repository.*;
import alfio.repository.user.AuthorityRepository;
import alfio.repository.user.OrganizationRepository;
Expand Down Expand Up @@ -795,7 +796,7 @@ public void updateTicketOwner(Ticket ticket,
}

private boolean isAdmin(Optional<UserDetails> userDetails) {
return userDetails.flatMap(u -> u.getAuthorities().stream().filter(a -> AuthorityRepository.ROLE_ADMIN.equals(a.getAuthority())).findFirst()).isPresent();
return userDetails.flatMap(u -> u.getAuthorities().stream().map(a -> Role.fromRoleName(a.getAuthority())).filter(Role.ADMIN::equals).findFirst()).isPresent();
}

private void sendTicketByEmail(Ticket ticket, Locale locale, Event event, PartialTicketTextGenerator confirmationTextBuilder, PartialTicketPDFGenerator pdfTemplateGenerator) {
Expand Down
53 changes: 39 additions & 14 deletions src/main/java/alfio/manager/user/UserManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,25 @@
*/
package alfio.manager.user;

import alfio.model.user.Authority;
import alfio.model.user.Organization;
import alfio.model.user.User;
import alfio.model.user.UserWithPassword;
import alfio.model.user.*;
import alfio.repository.user.AuthorityRepository;
import alfio.repository.user.OrganizationRepository;
import alfio.repository.user.UserRepository;
import alfio.repository.user.join.UserOrganizationRepository;
import alfio.util.PasswordGenerator;
import alfio.util.ValidationResult;
import ch.digitalfondue.npjt.AffectedRowCountAndKey;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.validation.ValidationUtils;

import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;

Expand Down Expand Up @@ -87,6 +85,26 @@ public User findUser(int id) {
return userRepository.findById(id);
}

public Collection<Role> getAvailableRoles(String username) {
User user = findUserByUsername(username);
if(isAdmin(user)) {
return EnumSet.of(Role.OWNER, Role.OPERATOR);
}
if(isOwner(user)) {
return EnumSet.of(Role.OPERATOR);
}
return Collections.emptySet();
}

/**
* Return the most privileged role of a user
* @param user
* @return user role
*/
public Role getUserRole(User user) {
return getUserAuthorities(user).stream().map(Authority::getRole).sorted().findFirst().orElse(Role.OPERATOR);
}

public List<Organization> findUserOrganizations(String username) {
return findUserOrganizations(userRepository.getByUsername(username));
}
Expand All @@ -110,11 +128,11 @@ public List<Organization> findUserOrganizations(User user) {
}

public boolean isAdmin(User user) {
return checkRole(user, a -> a.getRole().equals(AuthorityRepository.ROLE_ADMIN));
return checkRole(user, a -> a.getRole().equals(Role.ADMIN));
}

public boolean isOwner(User user) {
return checkRole(user, a -> a.getRole().equals(AuthorityRepository.ROLE_ADMIN) || a.getRole().equals(AuthorityRepository.ROLE_OWNER));
return checkRole(user, a -> a.getRole().equals(Role.ADMIN) || a.getRole().equals(Role.OWNER));
}

public boolean isOwnerOfOrganization(User user, int organizationId) {
Expand Down Expand Up @@ -151,20 +169,23 @@ public ValidationResult validateOrganization(Integer id, String name, String ema
}

@Transactional
public void editUser(int id, int organizationId, String username, String firstName, String lastName, String emailAddress) {
public void editUser(int id, int organizationId, String username, String firstName, String lastName, String emailAddress, Role role, String currentUsername) {
int userOrganizationResult = userOrganizationRepository.updateUserOrganization(id, organizationId);
Assert.isTrue(userOrganizationResult == 1, "unexpected error during organization update");
int userResult = userRepository.update(id, username, firstName, lastName, emailAddress);
Assert.isTrue(userResult == 1, "unexpected error during user update");
Assert.isTrue(getAvailableRoles(currentUsername).contains(role), "cannot assign role "+role);
authorityRepository.revokeAll(username);
authorityRepository.create(username, role.getRoleName());
}

@Transactional
public UserWithPassword insertUser(int organizationId, String username, String firstName, String lastName, String emailAddress) {
public UserWithPassword insertUser(int organizationId, String username, String firstName, String lastName, String emailAddress, Role role) {
Organization organization = organizationRepository.getById(organizationId);
String userPassword = PasswordGenerator.generateRandomPassword();
AffectedRowCountAndKey<Integer> result = userRepository.create(username, passwordEncoder.encode(userPassword), firstName, lastName, emailAddress, true);
userOrganizationRepository.create(result.getKey(), organization.getId());
authorityRepository.create(username, AuthorityRepository.ROLE_OPERATOR);
authorityRepository.create(username, role.getRoleName());
return new UserWithPassword(userRepository.findById(result.getKey()), userPassword, UUID.randomUUID().toString());
}

Expand All @@ -183,7 +204,7 @@ public void deleteUser(int userId, String currentUsername) {
Assert.isTrue(userRepository.toggleEnabled(userId, false) == 1, "unexpected update result");
}

public ValidationResult validateUser(Integer id, String username, int organizationId, String firstName, String lastName, String emailAddress) {
public ValidationResult validateUser(Integer id, String username, int organizationId, String role, String firstName, String lastName, String emailAddress) {
int userId = ID_EVALUATOR.apply(id);
final long existing = userRepository.findByUsername(username)
.stream()
Expand All @@ -192,7 +213,11 @@ public ValidationResult validateUser(Integer id, String username, int organizati
if(existing > 0) {
return ValidationResult.failed(new ValidationResult.ValidationError("username", "There is already another user with the same username."));
}
return ValidationResult.success();
return ValidationResult.of(Arrays.asList(Pair.of(firstName, "firstName"), Pair.of(lastName, "lastName"), Pair.of(emailAddress, "emailAddress"))
.stream()
.filter(p -> StringUtils.isEmpty(p.getKey()))
.map(p -> new ValidationResult.ValidationError(p.getKey(), p.getValue() + " is required"))
.collect(toList()));
}


Expand Down
3 changes: 3 additions & 0 deletions src/main/java/alfio/model/modification/UserModification.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class UserModification {

private final Integer id;
private final int organizationId;
private final String role;
private final String username;
private final String firstName;
private final String lastName;
Expand All @@ -33,12 +34,14 @@ public class UserModification {
@JsonCreator
public UserModification(@JsonProperty("id") Integer id,
@JsonProperty("organizationId") int organizationId,
@JsonProperty("role") String role,
@JsonProperty("username") String username,
@JsonProperty("firstName") String firstName,
@JsonProperty("lastName") String lastName,
@JsonProperty("emailAddress") String emailAddress) {
this.id = id;
this.organizationId = organizationId;
this.role = role;
this.username = username;
this.firstName = firstName;
this.lastName = lastName;
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/alfio/model/user/Authority.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
@Getter
public class Authority {
private final String username;
private final String role;
private final Role role;

public Authority(@Column("username") String username, @Column("role") String role) {
public Authority(@Column("username") String username, @Column("role") String roleName) {
this.username = username;
this.role = role;
this.role = Role.fromRoleName(roleName);
}
}
36 changes: 36 additions & 0 deletions src/main/java/alfio/model/user/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* This file is part of alf.io.
*
* alf.io is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* alf.io is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with alf.io. If not, see <http://www.gnu.org/licenses/>.
*/
package alfio.model.user;

import lombok.Getter;

import java.util.Arrays;

@Getter
public enum Role {
ADMIN("ROLE_ADMIN"), OWNER("ROLE_OWNER"), OPERATOR("ROLE_OPERATOR");

private final String roleName;

Role(String roleName) {
this.roleName = roleName;
}

public static Role fromRoleName(String roleName) {
return Arrays.stream(values()).filter(r -> r.getRoleName().equals(roleName)).findFirst().orElse(OPERATOR);
}
}
7 changes: 3 additions & 4 deletions src/main/java/alfio/repository/user/AuthorityRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,12 @@
@QueryRepository
public interface AuthorityRepository {

String ROLE_ADMIN = "ROLE_ADMIN";
String ROLE_OWNER = "ROLE_OWNER";
String ROLE_OPERATOR = "ROLE_OPERATOR";

@Query("SELECT * from authority where username = :username")
List<Authority> findGrantedAuthorities(@Bind("username") String username);

@Query("INSERT INTO authority(username, role) VALUES (:username, :role)")
int create(@Bind("username") String username, @Bind("role") String role);

@Query("DELETE from authority where username = :username")
int revokeAll(@Bind("username") String username);
}
7 changes: 7 additions & 0 deletions src/main/java/alfio/util/ValidationResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ public static ValidationResult failed(ValidationError... errors) {
return failed(Arrays.asList(errors));
}

public static ValidationResult of(List<ValidationError> errors) {
if(errors.size() > 0) {
return failed(errors);
}
return success();
}

public ValidationResult ifSuccess(Operation operation) {
if(errorCount == 0) {
operation.doIt();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
<select data-ng-model="editUserCtrl.user.organizationId" data-grab-focus name="organizationId" class="form-control" id="organizationId" data-required data-ng-options="organization.id as organization.name for organization in editUserCtrl.organizations"></select>
<field-error data-form-obj="editUser" data-field-obj="editUser.organizationId"></field-error>
</div>
<div class="form-group">
<label for="role">Role</label>
<select data-ng-model="editUserCtrl.user.role" name="role" class="form-control" id="role" data-required data-ng-options="role for role in editUserCtrl.roles"></select>
<field-error data-form-obj="editUser" data-field-obj="editUser.role"></field-error>
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" data-ng-model="editUserCtrl.user.username" name="username" id="username" class="form-control" required data-ng-minlength="2" />
Expand Down
14 changes: 9 additions & 5 deletions src/main/webapp/resources/js/admin/feature/user/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,13 @@
self.user = result;
});
}
var organizations = [];
self.user = {};
self.organizations = [];
OrganizationService.getAllOrganizations().success(function(result) {
organizations = result;
self.organizations = result;
self.roles = [];

$q.all([OrganizationService.getAllOrganizations(), UserService.getAllRoles()]).then(function(results) {
self.organizations = results[0].data;
self.roles = results[1].data;
});

self.save = function(form, user) {
Expand Down Expand Up @@ -130,7 +131,7 @@

}

EditUserController.$inject = ['$state', '$stateParams', '$rootScope', '$q', 'OrganizationService', 'UserService', 'ValidationService'];
EditUserController.$inject = ['$state', '$stateParams', '$rootScope', '$q', 'OrganizationService', 'UserService', 'ValidationService', '$q'];

function OrganizationService($http, HttpErrorHandler) {
return {
Expand All @@ -156,6 +157,9 @@

function UserService($http, $modal, $window, HttpErrorHandler) {
return {
getAllRoles: function() {
return $http.get('/admin/api/roles.json').error(HttpErrorHandler.handle);
},
getAllUsers : function() {
return $http.get('/admin/api/users.json').error(HttpErrorHandler.handle);
},
Expand Down
Loading

0 comments on commit b78a844

Please sign in to comment.