Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* end-to-end workflow for technical user creation when adding a non-existing user to a namespace:
   1. Admin edits collaborators in a given namespace
   2. Admin selects "add user"
   3. Given username does not exist
   4. A new modal opens, prompting admin to create a new technical user with a given authorization provider ID among supported
   5. Once created, modals close and the new technical user is created

* no tests present yet

Signed-off-by: Menahem Julien Raccah Lisei <[email protected]>
  • Loading branch information
menajrl committed Dec 20, 2019
1 parent fafe374 commit 629fae0
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 Contributors to the Eclipse Foundation
* Copyright (c) 2018, 2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
Expand Down Expand Up @@ -46,12 +46,23 @@ public interface IUserAccountService {
* @return status if the user was added successfully
*/
boolean addUserToTenant(String tenantId, String userId, Role ... roles);


/**
* Creates a technical user with the given id, authentication provider and roles, then adds it to
* the given tenant.
* @param tenantId
* @param userId
* @param authenticationProviderId
* @param roles
* @return always {@literal true} (fails with runtime exception if some operation failed).
*/
boolean createTechnicalUserAndAddToTenant(String tenantId, String userId, String authenticationProviderId, Role... roles);

/**
* Returns if the particular user as the role in the Tenant
*
* @param tenantId the tenant to check
* @param userId the user id
* @param authentication the authentication
* @param role the role to check (e.g ROLE_TENANT_ADMIN, ROLE_USER,..)
* @return
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 Contributors to the Eclipse Foundation
* Copyright (c) 2018, 2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
Expand Down Expand Up @@ -51,15 +51,18 @@ public class DefaultUserAccountService
@Value("${server.admin:#{null}}")
private String[] admins;

@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
private IUserRepository userRepository;

@Autowired
private INotificationService notificationService;

@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
private ITenantRepository tenantRepo;

@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
private ITenantUserRepo tenantUserRepo;

Expand Down Expand Up @@ -87,14 +90,37 @@ public boolean removeUserFromTenant(String tenantId, String userId) {
return true;
}

public boolean addUserToTenant(String tenantId, String userId, Role... roles) {
private Tenant validateAndReturnTenant(String tenantId, String userId, String authenticationProviderId, Role... roles) {
PreConditions.notNullOrEmpty(tenantId, "tenantId");
PreConditions.notNullOrEmpty(userId, "userId");
PreConditions.notNullOrEmpty(roles, "roles should not be empty");

if (authenticationProviderId != null) {
if (authenticationProviderId.trim().isEmpty()) {
throw new IllegalArgumentException("Given authentication provider cannot be empty.");
}
}
Tenant tenant = tenantRepo.findByTenantId(tenantId);

PreConditions.notNull(tenant, "Tenant with given tenantId doesnt exists");
return tenant;
}

public boolean createTechnicalUserAndAddToTenant(String tenantId, String userId, String authenticationProviderId, Role... roles) {

Tenant tenant = validateAndReturnTenant(tenantId, userId, authenticationProviderId, roles);
// this creates and persists the technical user
User user = User.create(userId, authenticationProviderId, null, true);
userRepository.save(user);
// this creates and persists the "tenant user"
TenantUser tenantUser = TenantUser.createTenantUser(tenant, user, roles);
tenantUserRepo.save(tenantUser);
eventPublisher.publishEvent(new AppEvent(this, userId, EventType.USER_ADDED));
return true;
}

public boolean addUserToTenant(String tenantId, String userId, Role... roles) {

// cannot validate authentication provider within context
Tenant tenant = validateAndReturnTenant(tenantId, userId, null, roles);

Optional<TenantUser> maybeUser = tenant.getUser(userId);
if (maybeUser.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 Contributors to the Eclipse Foundation
* Copyright (c) 2018, 2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
Expand All @@ -12,6 +12,8 @@
*/
package org.eclipse.vorto.repository.web.account;

import com.google.common.base.Strings;
import io.swagger.annotations.ApiParam;
import java.security.Principal;
import java.util.Arrays;
import java.util.Collection;
Expand All @@ -34,6 +36,7 @@
import org.eclipse.vorto.repository.tenant.ITenantService;
import org.eclipse.vorto.repository.upgrade.IUpgradeService;
import org.eclipse.vorto.repository.web.ControllerUtils;
import org.eclipse.vorto.repository.web.account.dto.TenantTechnicalUserDto;
import org.eclipse.vorto.repository.web.account.dto.TenantUserDto;
import org.eclipse.vorto.repository.web.account.dto.UserDto;
import org.slf4j.Logger;
Expand All @@ -52,8 +55,6 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.google.common.base.Strings;
import io.swagger.annotations.ApiParam;

@RestController
public class AccountController {
Expand Down Expand Up @@ -108,6 +109,48 @@ public ResponseEntity<Boolean> addOrUpdateUsersForTenant(
}
}

@RequestMapping(method = RequestMethod.POST, value = "/rest/tenants/{tenantId}/users/{userId}")
@PreAuthorize("hasRole('ROLE_SYS_ADMIN') or hasPermission(#tenantId, 'org.eclipse.vorto.repository.domain.Tenant', 'ROLE_TENANT_ADMIN')")
public ResponseEntity<Boolean> createTechnicalUserForTenant(
@ApiParam(value = "tenantId", required = true) @PathVariable String tenantId,
@ApiParam(value = "userId", required = true) @PathVariable String userId,
@RequestBody @ApiParam(value = "The user to be added to the tenant",
required = true) final TenantTechnicalUserDto user) {

if (Strings.nullToEmpty(userId).trim().isEmpty()) {
return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
}

if (Strings.nullToEmpty(tenantId).trim().isEmpty()) {
return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
}

if (Strings.nullToEmpty(user.getAuthenticationProviderId()).trim().isEmpty()) {
return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
}

if (user.getRoles().stream()
.anyMatch(role -> role.equals(UserRole.ROLE_SYS_ADMIN))) {
return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
}

try {
LOGGER.info("Creating technical user [" + userId + "] and adding to tenant [" + tenantId + "] with role(s) [" + Arrays.toString(user.getRoles().toArray()) + "]");
if (userId.equals(SecurityContextHolder.getContext().getAuthentication().getName())) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(
accountService.createTechnicalUserAndAddToTenant(tenantId, userId, user.getAuthenticationProviderId(), toRoles(user)),
HttpStatus.OK
);
} catch (IllegalArgumentException e) {
return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
} catch (Exception e) {
LOGGER.error("error at addOrUpdateUsersForTenant()", e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}

private Role[] toRoles(TenantUserDto user) {
Set<Role> roles = user.getRoles().stream()
.filter(role -> !(role.equals(UserRole.ROLE_SYS_ADMIN)))
Expand Down Expand Up @@ -237,6 +280,7 @@ public ResponseEntity<UserDto> getUser(
if (user != null) {
return new ResponseEntity<>(UserDto.fromUser(user), HttpStatus.OK);
} else {
// TODO return something to prompt front-end to request creating technical user with given authentication provider ID
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright (c) 2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.vorto.repository.web.account.dto;

/**
* Represents a payload for requests to perform both:
* <ul>
* <li>
* Create a new technical user with given name, roles and authentication provider ID
* </li>
* <li>
* Add to the given namespace
* </li>
* </ul>
* @author mena-bosch
*/
public class TenantTechnicalUserDto extends TenantUserDto {
private String authenticationProviderId;
public String getAuthenticationProviderId() {
return authenticationProviderId;
}
public void setAuthenticationProviderId(String value) {
this.authenticationProviderId = value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ repositoryControllers.controller("tenantUserManagementController",
[ "$rootScope", "$scope", "$http", "$uibModal", "$uibModalInstance", "tenant", "dialogConfirm",
function($rootScope, $scope, $http, $uibModal, $uibModalInstance, tenant, dialogConfirm) {

$scope.tenant = tenant;
$scope.tenant = tenant;
$scope.isRetrievingTenantUsers = false;
$scope.userTenantUsers = [];

$scope.cancel = function() {
$uibModalInstance.dismiss("Canceled.");
$uibModalInstance.dismiss("Canceled.");
};

$scope.$on("USER_CONTEXT_UPDATED", function(evt, data) {
// $scope.getTenants();
});

$scope.getTenantUsers = function(tenantId) {
$scope.isRetrievingTenantUsers = true;

$http.get("./rest/tenants/" + tenantId + "/users")
.then(function(result) {
$scope.isRetrievingTenantUsers = false;
Expand All @@ -28,9 +28,9 @@ repositoryControllers.controller("tenantUserManagementController",
// TODO : handling of failures
});
};

$scope.getTenantUsers($scope.tenant.tenantId);

$scope.newUser = function() {
return {
edit: false,
Expand All @@ -43,7 +43,7 @@ repositoryControllers.controller("tenantUserManagementController",
roleAdmin : false
};
};

$scope.editableUser = function(user) {
return {
edit: true,
Expand All @@ -56,8 +56,8 @@ repositoryControllers.controller("tenantUserManagementController",
roleAdmin : user.roles.includes("ROLE_TENANT_ADMIN")
};
};
$scope.createOrUpdateUser = function(user) {

$scope.createOrUpdateUser = function(user, tenant) {
var modalInstance = $uibModal.open({
animation: true,
templateUrl: "webjars/repository-web/dist/partials/admin/createOrUpdateUser.html",
Expand All @@ -67,12 +67,12 @@ repositoryControllers.controller("tenantUserManagementController",
user: function () {
return user;
},
tenantId: function() {
return $scope.tenant.tenantId;
tenant: function() {
return $scope.tenant;
}
}
});

modalInstance.result.finally(function(result) {
$scope.getTenantUsers($scope.tenant.tenantId);
$rootScope.init();
Expand Down Expand Up @@ -101,24 +101,62 @@ repositoryControllers.controller("tenantUserManagementController",
]);

repositoryControllers.controller("createOrUpdateUserController",
["$rootScope", "$scope", "$uibModalInstance", "$http", "user", "tenantId",
function($rootScope, $scope, $uibModalInstance, $http, user, tenantId) {
["$rootScope", "$scope", "$uibModal", "$uibModalInstance", "$http", "user", "tenant",
function($rootScope, $scope, $uibModal, $uibModalInstance, $http, user, tenant) {

$scope.mode = user.edit ? "Update" : "Add";
$scope.user = user;
$scope.tenantId = tenantId;
$scope.tenant = tenant;
$scope.isCurrentlyAddingOrUpdating = false;
$scope.errorMessage = "";
$scope.selectedAuthenticationProviderId = null;

$scope.cancel = function() {
$uibModalInstance.dismiss("Canceled.");
};


$scope.promptCreateNewTechnicalUser = function() {
var modalInstance = $uibModal.open({
animation: true,
templateUrl: "webjars/repository-web/dist/partials/admin/createTechnicalUser.html",
size: "md",
controller: "createOrUpdateUserController",
resolve: {
user: function () {
return user;
},
tenant: function() {
return tenant;
}
}
});

modalInstance.result.finally(function(result) {
$uibModalInstance.close($scope.user);
});
};

$scope.createNewTechnicalUser = function() {
$scope.isCurrentlyAddingOrUpdating = false;
$http.post("./rest/tenants/" + $scope.tenant.tenantId + "/users/" + $scope.user.username, {
"username": $scope.user.username,
"roles" : $scope.getRoles($scope.user),
"authenticationProviderId": $scope.selectedAuthenticationProviderId
})
.then(function(result) {
$uibModalInstance.close($scope.user);
}, function(reason) {
$scope.errorMessage = "Creation of technical user " +
$scope.user.username + " in namespace " +
$scope.tenant.defaultNamespace + " failed. ";
});
};

$scope.addOrUpdateUser = function() {
$scope.validate($scope.user, function(result) {
if (result.valid) {
$scope.isCurrentlyAddingOrUpdating = false;
$http.put("./rest/tenants/" + $scope.tenantId + "/users/" + $scope.user.username, {
$http.put("./rest/tenants/" + $scope.tenant.tenantId + "/users/" + $scope.user.username, {
"username": $scope.user.username,
"roles" : $scope.getRoles($scope.user)
})
Expand All @@ -128,7 +166,7 @@ repositoryControllers.controller("createOrUpdateUserController",
$scope.errorMessage = "You cannot change your own permissions.";
});
} else {
$scope.errorMessage = result.errorMessage;
$scope.promptCreateNewTechnicalUser();
}
});
};
Expand Down
Loading

0 comments on commit 629fae0

Please sign in to comment.