Commit 8a78e85b authored by LAVENIER's avatar LAVENIER
Browse files

[fix] Config: Rename option 'sumaris.supervisor.department' into...

[fix] Config: Rename option 'sumaris.supervisor.department' into 'sumaris.data.accessNotSelfData.department.ids'
parent 3ca75b9d
......@@ -36,9 +36,9 @@ public enum ExtractionWebConfigurationOption implements ConfigOptionDef {
String.class,
false),
AUTH_ROLE_NOT_SELF_EXTRACTION_ACCESS(
"sumaris.auth.notSelfExtractionAccess.role",
n("sumaris.config.option.auth.allExtractionTypeAccess.role.description"),
ACCESS_NOT_SELF_EXTRACTION_MIN_ROLE(
"sumaris.extraction.accessNotSelfExtraction.role",
n("sumaris.config.option.extraction.accessNotSelfExtraction.role.description"),
"ROLE_ADMIN", // Possible values: ROLE_GUEST, ROLE_USER, ROLE_SUPERVISOR, ROLE_ADMIN
String.class,
false),
......
......@@ -67,17 +67,17 @@ public class ExtractionSecurityServiceImpl implements ExtractionSecurityService
@Autowired
protected IAuthService<PersonVO> authService;
private String minRoleForNotSelfDataAccess;
private String accessNotSelfExtractionMinRole;
@EventListener({ConfigurationReadyEvent.class, ConfigurationUpdatedEvent.class})
protected void onConfigurationReady(ConfigurationEvent event) {
this.minRoleForNotSelfDataAccess = configuration.getApplicationConfig().getOption(ExtractionWebConfigurationOption.AUTH_ROLE_NOT_SELF_EXTRACTION_ACCESS.getKey());
this.accessNotSelfExtractionMinRole = configuration.getApplicationConfig().getOption(ExtractionWebConfigurationOption.ACCESS_NOT_SELF_EXTRACTION_MIN_ROLE.getKey());
}
@Override
public boolean canReadAll() {
return (StringUtils.isNotBlank(minRoleForNotSelfDataAccess)
&& authService.hasAuthority(minRoleForNotSelfDataAccess))
return (StringUtils.isNotBlank(accessNotSelfExtractionMinRole)
&& authService.hasAuthority(accessNotSelfExtractionMinRole))
|| authService.isAdmin();
}
......
......@@ -27,6 +27,8 @@ package net.sumaris.core.config;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableSet;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
......@@ -74,6 +76,11 @@ public class SumarisConfiguration extends PropertyPlaceholderConfigurer {
*/
protected final ApplicationConfig applicationConfig;
/**
* Cache for complexe options (e.g. for list of values, to avoid many call of split())
*/
protected final Cache<String, Object> complexOptionsCache = CacheBuilder.newBuilder().build();
private static SumarisConfiguration instance;
/**
......@@ -192,6 +199,10 @@ public class SumarisConfiguration extends PropertyPlaceholderConfigurer {
}
public void cleanCache() {
complexOptionsCache.invalidateAll();
}
/**
* Add alias to the given ApplicationConfig. <p/>
* This method could be override to add specific alias
......
......@@ -123,7 +123,7 @@ public class Application {
}
}
@Bean
@Bean("configuration")
public SumarisConfiguration configuration(ConfigurableEnvironment env) {
SumarisConfiguration config = SumarisConfiguration.getInstance();
......
......@@ -32,7 +32,7 @@ public interface ConfigurationService {
boolean isReady();
void updateConfigFromSoftwareProperties();
void applySoftwareProperties();
}
......@@ -126,7 +126,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
else {
// Update the config, from the software properties
updateConfigFromSoftwareProperties();
applySoftwareProperties();
// Publish ready event
if (event instanceof SchemaReadyEvent) {
......@@ -161,7 +161,10 @@ public class ConfigurationServiceImpl implements ConfigurationService {
ready = false;
// Update the config, from the software properties
updateConfigFromSoftwareProperties();
applySoftwareProperties();
// Clean config cache
configuration.cleanCache();
// Publish update event
publisher.publishEvent(new ConfigurationUpdatedEvent(configuration));
......@@ -189,7 +192,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
}
@Override
public void updateConfigFromSoftwareProperties() {
public void applySoftwareProperties() {
boolean newDatabase = false;
......
......@@ -28,7 +28,6 @@ import net.sumaris.core.model.referential.QualityFlagEnum;
import net.sumaris.core.model.referential.UserProfileEnum;
import net.sumaris.core.service.AbstractServiceTest;
import net.sumaris.core.service.administration.PersonService;
import net.sumaris.core.service.referential.ReferentialService;
import net.sumaris.core.vo.administration.user.PersonVO;
import org.junit.*;
import org.junit.runners.MethodSorters;
......@@ -53,7 +52,7 @@ public class ConfigurationServiceOracleTest extends AbstractServiceTest {
@Before
public void setup() {
// force apply software configuration
configurationService.updateConfigFromSoftwareProperties();
configurationService.applySoftwareProperties();
}
@Test
......
......@@ -26,6 +26,9 @@ import it.ozimov.springboot.mail.configuration.EnableEmailTools;
import lombok.extern.slf4j.Slf4j;
import net.sumaris.core.config.SumarisConfiguration;
import net.sumaris.core.config.SumarisConfigurationOption;
import net.sumaris.core.event.config.ConfigurationEvent;
import net.sumaris.core.event.config.ConfigurationReadyEvent;
import net.sumaris.core.event.config.ConfigurationUpdatedEvent;
import net.sumaris.core.exception.SumarisTechnicalException;
import net.sumaris.core.util.ApplicationUtils;
import net.sumaris.core.util.I18nUtil;
......@@ -42,6 +45,7 @@ import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfigurati
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
......@@ -84,7 +88,7 @@ public class Application extends SpringBootServletInitializer {
SpringApplication.run(Application.class, args);
}
@Bean
@Bean("configuration")
public static SumarisServerConfiguration configuration(ConfigurableEnvironment env) {
SumarisServerConfiguration.initDefault(env);
SumarisServerConfiguration config = SumarisServerConfiguration.getInstance();
......@@ -173,5 +177,4 @@ public class Application extends SpringBootServletInitializer {
protected static String getI18nBundleName() {
return "sumaris-server-i18n";
}
}
......@@ -24,10 +24,14 @@ package net.sumaris.server.config;
* #L%
*/
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import lombok.extern.slf4j.Slf4j;
import net.sumaris.core.config.SumarisConfiguration;
import net.sumaris.core.config.SumarisConfigurationOption;
import net.sumaris.server.http.security.AuthTokenTypeEnum;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.nuiton.config.ApplicationConfig;
import org.nuiton.version.VersionBuilder;
......@@ -35,7 +39,8 @@ import org.nuiton.version.Version;
import org.springframework.core.env.ConfigurableEnvironment;
import java.io.File;
import java.util.TimeZone;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p>SumarisServerConfiguration class.</p>
......@@ -98,16 +103,50 @@ public class SumarisServerConfiguration extends SumarisConfiguration {
super.overrideExternalModulesDefaultOptions(applicationConfig);
}
public int getSupervisorDepartment() {
return applicationConfig.getOptionAsInt(SumarisServerConfigurationOption.SUPERVISOR_DEPARTMENT.getKey());
public List<Integer> getAccessNotSelfDataDepartmentIds() {
final String optionKey = SumarisServerConfigurationOption.ACCESS_NOT_SELF_DATA_DEPARTMENT_IDS.getKey();
List<Integer> result = (List<Integer>) complexOptionsCache.getIfPresent(optionKey);
// Not exists in cache
if (result == null) {
String depIds = applicationConfig.getOption(optionKey);
if (StringUtils.isBlank(depIds)) {
result = ImmutableList.of();
}
else {
final List<String> invalidIds = Lists.newArrayList();
result = Splitter.on(",").omitEmptyStrings().trimResults()
.splitToList(depIds)
.stream()
.map(depId -> {
try {
return Integer.parseInt(depId);
}
catch (Exception e) {
invalidIds.add(depId);
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(invalidIds)) {
log.error("Skipping invalid values found in configuration option '{}': {}", optionKey, invalidIds);
}
}
// Add to options cache
complexOptionsCache.put(optionKey, result);
}
return result;
}
public String getAuthRoleForNotSelfData() {
return applicationConfig.getOption(SumarisServerConfigurationOption.AUTH_ROLE_NOT_SELF_DATA_ACCESS.getKey());
public String getAccessNotSelfDataMinRole() {
return applicationConfig.getOption(SumarisServerConfigurationOption.ACCESS_NOT_SELF_DATA_MIN_ROLE.getKey());
}
public String getAuthRoleForNotSelfExtraction() {
return applicationConfig.getOption(SumarisServerConfigurationOption.AUTH_ROLE_NOT_SELF_EXTRACTION_ACCESS.getKey());
public String getAccessNotSelfExtractionMinRole() {
return applicationConfig.getOption(SumarisServerConfigurationOption.ACCESS_NOT_SELF_EXTRACTION_MIN_ROLE.getKey());
}
public boolean enableAuthToken() {
......
......@@ -129,14 +129,21 @@ public enum SumarisServerConfigurationOption implements ConfigOptionDef {
Integer.class,
false),
AUTH_ROLE_NOT_SELF_DATA_ACCESS(
"sumaris.auth.notSelfDataAccess.role",
n("sumaris.config.option.auth.notSelfDataAccess.role.description"),
ACCESS_NOT_SELF_DATA_MIN_ROLE(
"sumaris.data.accessNotSelfData.role",
n("sumaris.config.option.data.accessNotSelfData.role.description"),
"ROLE_ADMIN", // Possible values: ROLE_GUEST, ROLE_USER, ROLE_SUPERVISOR, ROLE_ADMIN
String.class,
false),
AUTH_ROLE_NOT_SELF_EXTRACTION_ACCESS(ExtractionWebConfigurationOption.AUTH_ROLE_NOT_SELF_EXTRACTION_ACCESS),
ACCESS_NOT_SELF_DATA_DEPARTMENT_IDS(
"sumaris.data.accessNotSelfData.department.ids",
n("sumaris.config.option.data.accessNotSelfData.department.ids.description"),
null,
Integer.class,
false),
ACCESS_NOT_SELF_EXTRACTION_MIN_ROLE(ExtractionWebConfigurationOption.ACCESS_NOT_SELF_EXTRACTION_MIN_ROLE),
SECURITY_LDAP_ENABLED(
"spring.security.ldap.enabled",
......@@ -162,13 +169,6 @@ public enum SumarisServerConfigurationOption implements ConfigOptionDef {
null, // NUll == auto detected
String.class),
SUPERVISOR_DEPARTMENT(
"sumaris.supervisor.department",
n("sumaris.config.option.supervisor.department.description"),
null,
Integer.class,
false),
APP_MIN_VERSION(
"sumaris.app.version.min",
n("sumaris.config.option.sumaris.app.version.min.description"),
......
......@@ -26,6 +26,7 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import io.leangen.graphql.annotations.*;
import io.leangen.graphql.execution.ResolutionEnvironment;
import lombok.NonNull;
import net.sumaris.core.dao.referential.metier.MetierRepository;
import net.sumaris.core.dao.technical.Page;
import net.sumaris.core.dao.technical.Pageables;
......@@ -259,7 +260,6 @@ public class DataGraphQLService {
) {
filter = fillTripFilterDefaults(filter);
SortDirection sortDirection = SortDirection.fromString(direction, SortDirection.DESC);
// Read from trash
......@@ -276,6 +276,8 @@ public class DataGraphQLService {
TripVO.class).getContent();
}
filter = fillRootDataFilter(filter, TripFilterVO.class);
// Set default sort
sort = sort != null ? sort : TripVO.Fields.DEPARTURE_DATE_TIME;
......@@ -294,8 +296,8 @@ public class DataGraphQLService {
@GraphQLQuery(name = "tripsCount", description = "Get trips count")
@Transactional(readOnly = true)
@IsUser
public long getTripsCount(@GraphQLArgument(name = "filter") TripFilterVO filter,
@GraphQLArgument(name = "trash", defaultValue = "false") Boolean trash) {
public long countTrips(@GraphQLArgument(name = "filter") TripFilterVO filter,
@GraphQLArgument(name = "trash", defaultValue = "false") Boolean trash) {
if (trash) {
// Check user is admin
checkIsAdmin("Cannot access to trash");
......@@ -304,7 +306,9 @@ public class DataGraphQLService {
return trashService.count(Trip.class.getSimpleName());
}
return tripService.countByFilter(fillTripFilterDefaults(filter));
filter = fillRootDataFilter(filter, TripFilterVO.class);
return tripService.countByFilter(filter);
}
@GraphQLQuery(name = "trip", description = "Get a trip, by id")
......@@ -517,7 +521,6 @@ public class DataGraphQLService {
@GraphQLArgument(name = "trash", defaultValue = "false") Boolean trash,
@GraphQLEnvironment ResolutionEnvironment env
) {
filter = fillObserveLocationFilterDefaults(filter);
SortDirection sortDirection = SortDirection.fromString(direction, SortDirection.DESC);
// Read from trash
......@@ -534,6 +537,8 @@ public class DataGraphQLService {
ObservedLocationVO.class).getContent();
}
filter = fillRootDataFilter(filter, ObservedLocationFilterVO.class);
Set<String> fields = GraphQLUtils.fields(env);
final List<ObservedLocationVO> result = observedLocationService.findAll(
filter,
......@@ -550,11 +555,8 @@ public class DataGraphQLService {
@GraphQLQuery(name = "observedLocationsCount", description = "Get total number of observed locations")
@Transactional(readOnly = true)
@IsUser
public long getObservedLocationsCount(@GraphQLArgument(name = "filter") ObservedLocationFilterVO filter,
@GraphQLArgument(name = "trash", defaultValue = "false") Boolean trash) {
filter = fillObserveLocationFilterDefaults(filter);
public long countObservedLocations(@GraphQLArgument(name = "filter") ObservedLocationFilterVO filter,
@GraphQLArgument(name = "trash", defaultValue = "false") Boolean trash) {
if (trash) {
// Check user is admin
checkIsAdmin("Cannot access to trash");
......@@ -563,6 +565,8 @@ public class DataGraphQLService {
return trashService.count(ObservedLocation.class.getSimpleName());
}
filter = fillRootDataFilter(filter, ObservedLocationFilterVO.class);
return observedLocationService.count(filter);
}
......@@ -1392,52 +1396,47 @@ public class DataGraphQLService {
.build();
}
protected TripFilterVO fillTripFilterDefaults(TripFilterVO filter) {
TripFilterVO result = filter != null ? filter : new TripFilterVO();
// Restrict to self data and/or department data
PersonVO user = authService.getAuthenticatedUser().orElse(null);
if (user != null) {
if (!canAccessNotSelfData()) {
result.setRecorderPersonId(user.getId());
}
if (!canAccessNotSelfDepartmentData(user)) {
result.setRecorderDepartmentId(user.getDepartment().getId());
}
} else {
result.setRecorderPersonId(-999); // Hide all. Should never occur
/**
* Restrict to self data and/or department data
* @param filter
*/
protected <F extends IRootDataFilter> F fillRootDataFilter(F filter, Class<F> filterClass) {
try {
filter = filter != null ? filter : filterClass.newInstance();
}
catch (InstantiationException | IllegalAccessException e) {
log.error("Cannot create filter instance: {}", e.getMessage(), e);
}
return result;
}
protected ObservedLocationFilterVO fillObserveLocationFilterDefaults(ObservedLocationFilterVO filter) {
ObservedLocationFilterVO result = filter != null ? filter : new ObservedLocationFilterVO();
// Restrict to self data and/or department data
PersonVO user = authService.getAuthenticatedUser().orElse(null);
if (user != null) {
if (!canAccessNotSelfData()) {
result.setRecorderPersonId(user.getId());
if (!canUserAccessNotSelfData()) {
// Limit data access to self data
filter.setRecorderPersonId(user.getId());
}
if (!canAccessNotSelfDepartmentData(user)) {
result.setRecorderDepartmentId(user.getDepartment().getId());
else {
Integer depId = user.getDepartment().getId();
if (!canDepartmentAccessNotSelfData(depId)) {
// Limit data access to user's department
filter.setRecorderDepartmentId(depId);
}
}
} else {
result.setRecorderPersonId(-999); // Hide all. Should never occur
filter.setRecorderPersonId(-999); // Hide all. Should never occur
}
return result;
return filter;
}
protected boolean canAccessNotSelfData() {
String minRole = config.getAuthRoleForNotSelfData();
protected boolean canUserAccessNotSelfData() {
String minRole = config.getAccessNotSelfDataMinRole();
return StringUtils.isBlank(minRole) || authService.hasAuthority(minRole);
}
protected boolean canAccessNotSelfDepartmentData(PersonVO user) {
int supervisorDepartment = config.getSupervisorDepartment();
return supervisorDepartment == 0 || supervisorDepartment == user.getDepartment().getId();
protected boolean canDepartmentAccessNotSelfData(@NonNull Integer actualDepartmentId) {
List<Integer> expectedDepartmentIds = config.getAccessNotSelfDataDepartmentIds();
return CollectionUtils.isEmpty(expectedDepartmentIds) || expectedDepartmentIds.contains(actualDepartmentId);
}
/**
......
......@@ -48,6 +48,10 @@ public interface AuthService extends IAuthService<PersonVO> {
*/
boolean hasAuthority(String authority);
default boolean isLogin() {
return getAuthenticatedUser().isPresent();
}
default boolean isGuest() {
return hasAuthority(AUTHORITY_PREFIX + UserProfileEnum.GUEST.label);
}
......
......@@ -89,12 +89,12 @@
<SOFTWARE_PROPERTY ID="106" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.extraction.enable" NAME="true"/>
<SOFTWARE_PROPERTY ID="107" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.extraction.map.enable" NAME="false"/>
<SOFTWARE_PROPERTY ID="108" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.extraction.product.enable" NAME="false"/>
<SOFTWARE_PROPERTY ID="109" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.auth.notSelfExtractionAccess.role" NAME="ROLE_USER"/>
<SOFTWARE_PROPERTY ID="109" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.extraction.accessNotSelfExtraction.role" NAME="ROLE_USER"/>
<SOFTWARE_PROPERTY ID="110" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.referential.vessel.enable" NAME="false"/>
<SOFTWARE_PROPERTY ID="111" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.analyticReferences.enable" NAME="true"/>
<SOFTWARE_PROPERTY ID="112" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.persistence.technicalTables.update" NAME="false"/>
<SOFTWARE_PROPERTY ID="113" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.menu.items" NAME="[{&#34;title&#34;: &#34;Lignes de plan&#34;, &#34;path&#34;: &#34;/referential/programs/40/strategies&#34;,&#34;before&#34;:&#34;MENU.OCCASIONS&#34;, &#34;icon&#34;: &#34;contract&#34;, &#34;profile&#34;: &#34;USER&#34;}]"/>
<SOFTWARE_PROPERTY ID="114" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.auth.notSelfDataAccess.role" NAME="ROLE_USER"/>
<SOFTWARE_PROPERTY ID="115" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.supervisor.department" NAME="3"/>
<SOFTWARE_PROPERTY ID="114" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.data.accessNotSelfData.role" NAME="ROLE_USER"/>
<SOFTWARE_PROPERTY ID="115" STATUS_FK="1" SOFTWARE_FK="5" CREATION_DATE="2019-02-11" LABEL="sumaris.data.accessNotSelfData.department.ids" NAME="3"/>
</dataset>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment