Code development platform for open source projects from the European Union institutions :large_blue_circle: EU Login authentication by SMS will be completely phased out by mid-2025. To see alternatives please check here

Skip to content
Snippets Groups Projects
Commit 9b6d9355 authored by Joze RIHTARSIC's avatar Joze RIHTARSIC
Browse files

Pull request #113: [REST API] Set new HTTP headers for visibility and domain group

Merge in EDELIVERY/smp from bugfix/EDELIVERY-13823-new-http-header-to-define-user-group to development

* commit '4dff5811':
  [REST API] Set new HTTP headers for visibility and domain group
parents cb905482 4dff5811
No related branches found
No related tags found
No related merge requests found
Pipeline #198219 failed
Showing
with 158 additions and 27 deletions
eDelivery SMP 5.1
- Added the HTTP parameter 'Resource-Owner' as alternative to ServiceGroup-Owner
- added new HTTP headers when creating a new resource:
'Resource-Owner' as alternative to ServiceGroup-Owner
'Resource-Group': the name of the group: to define to which group in domain the resource belongs to. The group must be in one of the admin's groups. If not given the first group from the list is used.
'Resource-Visibility': The visibility of the resource: PUBLIC, PRIVATE. The default value is PUBLIC. Resource visibility can be set only at creation time. To change value of the existing resource it must be done via the UI.
- added new properties:
smp.instance.name: The SMP instance name
smp.credentials.reset_request.url: The URL to reset the user password
......
......@@ -60,7 +60,8 @@ insert into SMP_DOMAIN_RESOURCE_DEF (ID, FK_RESOURCE_DEF_ID, FK_DOMAIN_ID,CREATE
(2, 2, 1, NOW(), NOW());
insert into SMP_GROUP (ID, FK_DOMAIN_ID, NAME, VISIBILITY, CREATED_ON, LAST_UPDATED_ON) values
(1, 1, 'test group', 'PUBLIC', NOW(), NOW());
(1, 1, 'Group001', 'PUBLIC', NOW(), NOW()),
(2, 1, 'Group002', 'PUBLIC', NOW(), NOW());
insert into SMP_GROUP_MEMBER (ID, FK_GROUP_ID, FK_USER_ID, MEMBERSHIP_ROLE, CREATED_ON, LAST_UPDATED_ON) values
(1, 1, 2, 'ADMIN', NOW(), NOW()),
......
......@@ -48,17 +48,6 @@ import static org.apache.commons.lang3.StringUtils.trim;
@Repository
public class GroupDao extends BaseDao<DBGroup> {
/**
* Returns domain records from smp_domain table.
*
* @return the list of domain records from smp_domain table
* @throws IllegalStateException if no domain is configured
*/
public List<DBGroup> getAllGroups() {
TypedQuery<DBGroup> query = memEManager.createNamedQuery(QUERY_GROUP_ALL, DBGroup.class);
return query.getResultList();
}
/**
* Get group list for domains
*
......
......@@ -39,5 +39,21 @@ public enum VisibilityType {
/**
* Access to the domain, group or resource is possible only if you are only direct or un-direct member of the domain, group or resource
*/
PRIVATE
PRIVATE;
/**
* Returns the VisibilityType from the string case-insensitive value. If value is blank or null, null is returned.
*
* @param value the value
* @return the VisibilityType
* @IllegalArgumentException if the value is not valid.
*/
public static VisibilityType fromString(String value) {
String sValue = value == null ? null : value.trim();
if (sValue == null || sValue.isEmpty()) {
return null;
}
return valueOf(sValue.toUpperCase());
}
}
......@@ -8,9 +8,9 @@
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
*
* [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and limitations under the Licence.
......@@ -19,14 +19,16 @@
package eu.europa.ec.edelivery.smp.services.resource;
import eu.europa.ec.edelivery.smp.auth.SMPUserDetails;
import eu.europa.ec.edelivery.smp.services.IdentifierService;
import eu.europa.ec.edelivery.smp.data.dao.*;
import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType;
import eu.europa.ec.edelivery.smp.data.model.DBDomain;
import eu.europa.ec.edelivery.smp.data.model.DBGroup;
import eu.europa.ec.edelivery.smp.data.model.doc.DBDocument;
import eu.europa.ec.edelivery.smp.data.model.doc.DBResource;
import eu.europa.ec.edelivery.smp.data.model.doc.DBSubresource;
import eu.europa.ec.edelivery.smp.data.model.ext.DBResourceDef;
import eu.europa.ec.edelivery.smp.data.model.ext.DBSubresourceDef;
import eu.europa.ec.edelivery.smp.data.model.user.DBUser;
import eu.europa.ec.edelivery.smp.exceptions.ErrorCode;
import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
import eu.europa.ec.edelivery.smp.identifiers.Identifier;
......@@ -34,6 +36,7 @@ import eu.europa.ec.edelivery.smp.logging.SMPLogger;
import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
import eu.europa.ec.edelivery.smp.security.ResourceGuard;
import eu.europa.ec.edelivery.smp.services.ConfigurationService;
import eu.europa.ec.edelivery.smp.services.IdentifierService;
import eu.europa.ec.edelivery.smp.servlet.ResourceAction;
import eu.europa.ec.edelivery.smp.servlet.ResourceRequest;
import org.apache.commons.lang3.StringUtils;
......@@ -46,8 +49,7 @@ import java.util.Optional;
import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.SML_INVALID_IDENTIFIER;
import static eu.europa.ec.edelivery.smp.logging.SMPLogger.SECURITY_MARKER;
import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.join;
import static org.apache.commons.lang3.StringUtils.*;
/**
......@@ -66,6 +68,7 @@ public class ResourceResolverService {
final ConfigurationService configurationService;
final IdentifierService identifierService;
final DomainDao domainDao;
final GroupDao groupDao;
final ResourceDefDao resourceDefinitionDao;
final DomainResourceDefDao domainResourceDefDao;
final ResourceDao resourceDao;
......@@ -76,6 +79,7 @@ public class ResourceResolverService {
ConfigurationService configurationService,
IdentifierService identifierService,
DomainDao domainDao,
GroupDao groupDao,
DomainResourceDefDao domainResourceDefDao,
ResourceDefDao resourceDefinitionDao,
ResourceDao resourceDao,
......@@ -85,6 +89,7 @@ public class ResourceResolverService {
this.configurationService = configurationService;
this.identifierService = identifierService;
this.domainDao = domainDao;
this.groupDao = groupDao;
this.domainResourceDefDao = domainResourceDefDao;
this.resourceDefinitionDao = resourceDefinitionDao;
this.resourceDao = resourceDao;
......@@ -134,6 +139,12 @@ public class ResourceResolverService {
throw new SMPRuntimeException(ErrorCode.SG_NOT_EXISTS, resourceId.getValue(), resourceId.getScheme());
}
resource = createNewResource(resourceId, resourceDef, domain);
// determine the group for the resource
DBGroup group = resolveResourceGroup(user.getUser(), domain,
trimToNull(resourceRequest.getResourceGroupParameter()));
resource.setVisibility(resourceRequest.getResourceVisibilityParameter());
resource.setGroup(group);
}
locationVector.setResource(resource);
......@@ -282,6 +293,41 @@ public class ResourceResolverService {
return optResource.orElse(null);
}
/**
* Method resolves the group for the given domain, admin user and group name.
* If the group name is null/not given, the first group is returned. If the group name is provided
* but the user is not admin for the group, the exception is thrown.
*
* @param user admin user creating the resource
* @param domain domain where the resource is created
* @param domainGroup group name
* @return DBGroup for the given domain, user and group name.
* @throws SMPRuntimeException if the user is not admin authorized to create the resource for the given group/domain
*/
protected DBGroup resolveResourceGroup(DBUser user, DBDomain domain, String domainGroup) {
LOG.debug("Resolve group for domain [{}] and user [{}] and group [{}]", domain.getDomainCode(),
user.getUsername(),
domainGroup);
List<DBGroup> adminListGroup =
groupDao.getGroupsByDomainUserIdAndGroupRoles(domain.getId(), user.getId(), MembershipRoleType.ADMIN);
if (adminListGroup.isEmpty()) {
throw new SMPRuntimeException(ErrorCode.UNAUTHORIZED,
"User [" + user.getUsername() + "] is not admin for any group in domain [" + domain.getDomainCode() + "]");
}
if (domainGroup == null) {
LOG.debug("Set first/default group [{}] for domain [{}]", adminListGroup.get(0).getGroupName(),
domain.getDomainCode());
return adminListGroup.get(0);
}
return groupDao.getGroupsByDomainUserIdAndGroupRoles(domain.getId(), user.getId(), MembershipRoleType.ADMIN)
.stream()
.filter(group -> equalsIgnoreCase(group.getGroupName(), domainGroup))
.findFirst()
.orElseThrow(() -> new SMPRuntimeException(ErrorCode.UNAUTHORIZED,
"User [" + user.getUsername() + "] is not authorized for group ["
+ domainGroup + "] in domain [" + domain.getDomainCode() + "]"));
}
/**
* Resolve subresource for given resource , subresource context and subresouce Identifier
*
......
......@@ -8,9 +8,9 @@
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
*
* [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and limitations under the Licence.
......@@ -18,6 +18,7 @@
*/
package eu.europa.ec.edelivery.smp.servlet;
import eu.europa.ec.edelivery.smp.data.enums.VisibilityType;
import eu.europa.ec.edelivery.smp.data.model.DBDomain;
import eu.europa.ec.edelivery.smp.logging.SMPLogger;
import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
......@@ -37,7 +38,6 @@ import static org.apache.commons.lang3.StringUtils.trim;
*
* @author Joze Rihtarsic
* @since 5.0
*
*/
public class ResourceRequest {
private static final SMPLogger LOG = SMPLoggerFactory.getLogger(ResourceRequest.class);
......@@ -88,6 +88,22 @@ public class ResourceRequest {
return getHeader(WebConstants.HTTP_PARAM_RESOURCE_TYPE);
}
/**
* Returns the visibility of the resource. If value can not be parsed,
* the IllegalArgumentException is thrown.
* If the visibility is not set, the default value is PUBLIC.
* @return the visibility of the resource
* @throws IllegalArgumentException if the value can not be parsed.
*/
public VisibilityType getResourceVisibilityParameter() {
VisibilityType visibility = VisibilityType.fromString(getHeader(WebConstants.HTTP_PARAM_RESOURCE_VISIBILITY));
return visibility == null ? VisibilityType.PUBLIC : visibility;
}
public String getResourceGroupParameter() {
return getHeader(WebConstants.HTTP_PARAM_RESOURCE_GROUP);
}
public List<String> getUrlPathParameters() {
return urlPathParameters;
}
......
......@@ -31,6 +31,8 @@ public class WebConstants {
public static final String HTTP_PARAM_RESOURCE_TYPE = "Resource-Type";
public static final String HTTP_PARAM_OWNER_OBSOLETE = "ServiceGroup-Owner";
public static final String HTTP_PARAM_OWNER = "Resource-Owner";
public static final String HTTP_PARAM_RESOURCE_GROUP = "Resource-Group";
public static final String HTTP_PARAM_RESOURCE_VISIBILITY = "Resource-Visibility";
private WebConstants() {
}
......
......@@ -70,6 +70,8 @@ public class ResourceController {
private static final List<String> SUPPORTED_HEADERS = Arrays.asList(lowerCase(HTTP_PARAM_DOMAIN),
lowerCase(HTTP_PARAM_OWNER),
lowerCase(HTTP_PARAM_OWNER_OBSOLETE),
lowerCase(HTTP_PARAM_RESOURCE_GROUP),
lowerCase(HTTP_PARAM_RESOURCE_VISIBILITY),
lowerCase(HTTP_PARAM_RESOURCE_TYPE));
final ResourceService resourceService;
final DomainGuard domainGuard;
......
......@@ -54,7 +54,8 @@ public class ResourceControllerSingleDomainTest extends AbstractControllerTest {
private static final String SERVICE_GROUP_INPUT_BODY = getSampleServiceGroupBodyWithScheme(IDENTIFIER_SCHEME);
private static final String HTTP_HEADER_KEY_DOMAIN = "Domain";
private static final String HTTP_HEADER_KEY_SERVICE_GROUP_OWNER = "ServiceGroup-Owner";
private static final String HTTP_HEADER_KEY_RESOURCE_GROUP = "Resource-Group";
private static final String HTTP_HEADER_KEY_RESOURCE_VISIBILITY = "Resource-Visibility";
private static final String OTHER_OWNER_NAME = "CN=EHEALTH_SMP_TEST_BRAZIL,O=European Commission,C=BE:48b681ee8e0dcc08";
@BeforeEach
......@@ -190,4 +191,57 @@ public class ResourceControllerSingleDomainTest extends AbstractControllerTest {
.content(SERVICE_GROUP_INPUT_BODY))
.andExpect(status().isCreated());
}
@ParameterizedTest
@CsvSource({"Null group,,201",
"Empty group, '',201",
"Existing group:,'domain group',201",
"Existing group 2:,'Third group',201",
"Not authorized for domain:,'Second group',401",
"Not authorized for non existing domain:,'Not exists group',401"})
void createResourceForGroup(String testDesc, String groupName, int httpCode) throws Exception {
LOG.info(testDesc);
// create service group by group admin
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HTTP_HEADER_KEY_SERVICE_GROUP_OWNER, OTHER_OWNER_NAME);
httpHeaders.add(HTTP_HEADER_KEY_DOMAIN, "domain");
if (groupName != null) {
httpHeaders.add(HTTP_HEADER_KEY_RESOURCE_GROUP, groupName);
}
mvc.perform(put(URL_PATH)
.with(ADMIN_CREDENTIALS)
.contentType(APPLICATION_XML_VALUE)
.headers(httpHeaders)
.content(SERVICE_GROUP_INPUT_BODY))
.andExpect( status().is(httpCode) );
}
@ParameterizedTest
@CsvSource({"Null Visibility,,201",
"Empty Visibility, '',201",
"Public Visibility:,'PUBLIC',201",
"Private Visibility:,'PRIVATE',201",
"Case insensitive Visibility:,'PRiVaTE',201",
"Invalid Visibility:,'NotOKValue',400",
})
void createResourceWithVisibility(String testDesc, String visibility, int httpCode) throws Exception {
LOG.info(testDesc);
// create service group by group admin
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HTTP_HEADER_KEY_SERVICE_GROUP_OWNER, OTHER_OWNER_NAME);
httpHeaders.add(HTTP_HEADER_KEY_DOMAIN, "domain");
if (visibility != null) {
httpHeaders.add(HTTP_HEADER_KEY_RESOURCE_VISIBILITY, visibility);
}
mvc.perform(put(URL_PATH)
.with(ADMIN_CREDENTIALS)
.contentType(APPLICATION_XML_VALUE)
.headers(httpHeaders)
.content(SERVICE_GROUP_INPUT_BODY))
.andExpect( status().is(httpCode) );
}
}
......@@ -62,8 +62,8 @@ class GroupEditControllerIT extends AbstractControllerTest {
@ParameterizedTest
@CsvSource({
", 2",
"'', 2",
", 3",
"'', 3",
"group-admin, 1",
"resource-admin, 1",
"group-viewer, 0",
......
......@@ -86,7 +86,8 @@ insert into SMP_DOMAIN (ID, VISIBILITY, DOMAIN_CODE, SML_SUBDOMAIN, SML_SMP_ID,
insert into SMP_GROUP (ID, FK_DOMAIN_ID, NAME, VISIBILITY, CREATED_ON, LAST_UPDATED_ON) values
(1, 1, 'domain group', 'PUBLIC', NOW(), NOW()),
(2, 1, 'Second group', 'PUBLIC', NOW(), NOW());
(2, 1, 'Second group', 'PUBLIC', NOW(), NOW()),
(3, 1, 'Third group', 'PUBLIC', NOW(), NOW());
-- --------------
-- configure extension and document types service group and servicemetadata
......@@ -127,7 +128,8 @@ insert into SMP_DOMAIN_MEMBER (ID, FK_DOMAIN_ID, FK_USER_ID, MEMBERSHIP_ROLE, CR
insert into SMP_GROUP_MEMBER (ID, FK_GROUP_ID, FK_USER_ID, MEMBERSHIP_ROLE, CREATED_ON, LAST_UPDATED_ON) values
(1, 1, 1, 'ADMIN', NOW(), NOW()),
(2, 1, 3, 'ADMIN', NOW(), NOW());
(2, 3, 1, 'ADMIN', NOW(), NOW()),
(3, 1, 3, 'ADMIN', NOW(), NOW());
-- set ownership
insert into SMP_RESOURCE_MEMBER (ID, FK_RESOURCE_ID, FK_USER_ID, MEMBERSHIP_ROLE, CREATED_ON, LAST_UPDATED_ON) values
(-1, -1, 1, 'ADMIN', NOW(), NOW()),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment