Commit 8ecd62f1 authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch '153-repository-permissions' into 'master'

Resolve "Repository permissions"

Closes #153

See merge request !144
parents 5c56d0fd df063992
......@@ -26,7 +26,10 @@ public enum UserRole implements GrantedAuthority {
USER,
/** The administrator. */
ADMINISTRATOR;
ADMINISTRATOR,
/** Everyone role */
EVERYONE;
/**
* GrantedAuthorities start with ROLE_.
......
......@@ -27,6 +27,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys.catalog.exceptions.InvalidApiUsageException;
import org.genesys.catalog.exceptions.NotFoundElement;
import org.genesys.catalog.model.dataset.AccessionIdentifier;
......@@ -75,6 +76,9 @@ public class DatasetServiceImpl implements DatasetService {
@Autowired
private RepositoryService fileRepoService;
@Autowired
private CustomAclService aclService;
/** The utils. */
@Autowired
private Utils utils;
......@@ -404,6 +408,15 @@ public class DatasetServiceImpl implements DatasetService {
// throw new PublishValidationException(errorMap);
loaded.setPublished(true);
{
// Relax permissions on dataset files: allow USERS and ANONYMOUS to read the
// files
for (RepositoryFile datasetFile : loaded.getRepositoryFiles()) {
aclService.makePubliclyReadable(datasetFile, true);
}
}
return lazyLoad(datasetRepository.save(loaded));
}
......@@ -418,6 +431,14 @@ public class DatasetServiceImpl implements DatasetService {
throw new NotFoundElement("No dataset with specified uuid and version");
}
loaded.setPublished(false);
{
// Tighten permissions on dataset files
for (RepositoryFile datasetFile : loaded.getRepositoryFiles()) {
aclService.makePubliclyReadable(datasetFile, false);
}
}
return lazyLoad(datasetRepository.save(loaded));
}
......
......@@ -15,6 +15,7 @@
*/
package org.genesys.catalog.service.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
......@@ -67,6 +68,11 @@ public class UserServiceImpl extends BasicUserServiceImpl<UserRole, User> implem
public UserRole getDefaultUserRole() {
return UserRole.USER;
}
@Override
public Collection<UserRole> getDefaultUserRoles() {
return Sets.newHashSet(UserRole.USER, UserRole.EVERYONE);
}
@Override
public List<UserRole> listAvailableRoles() {
......
......@@ -82,14 +82,14 @@ public class UserServiceTest extends ServiceTest {
@Test
public void grantRoles() throws NotUniqueUserException, PasswordPolicyException, UserException {
User u = userService.createUser(USER_EMAIL, null, USER_PASSWORD, BasicUser.AccountType.LOCAL);
assertThat("Account must have default role assigned", u.getRoles(), containsInAnyOrder(UserRole.USER));
assertThat("Account must have default role assigned", u.getRoles(), containsInAnyOrder(UserRole.USER, UserRole.EVERYONE));
u = userService.setRoles(u, Sets.newHashSet(UserRole.USER, UserRole.ADMINISTRATOR));
assertThat("Account must have USER and ADMINISTRATOR roles", u.getRoles(), containsInAnyOrder(UserRole.USER, UserRole.ADMINISTRATOR));
assertThat("Account must have USER and ADMINISTRATOR roles", u.getRoles(), containsInAnyOrder(UserRole.USER, UserRole.EVERYONE, UserRole.ADMINISTRATOR));
u = userService.getUser(u.getId());
assertThat("Account must have USER and ADMINISTRATOR roles", u.getRoles(), containsInAnyOrder(UserRole.USER, UserRole.ADMINISTRATOR));
assertThat("Account must have USER and ADMINISTRATOR roles", u.getRoles(), containsInAnyOrder(UserRole.USER, UserRole.EVERYONE, UserRole.ADMINISTRATOR));
userService.setRoles(u, Sets.newHashSet(UserRole.USER));
u = userService.getUser(u.getId());
assertThat("Account must have USER role", u.getRoles(), containsInAnyOrder(UserRole.USER));
assertThat("Account must have USER role", u.getRoles(), containsInAnyOrder(UserRole.USER, UserRole.EVERYONE));
}
}
......@@ -59,8 +59,8 @@ public class OAuth2ServerConfig {
@Value("${default.oauth.refreshToken.validity}")
private int refreshTokenValiditySeconds;
@Value("${default.jwt.signingKey}")
private String jwtSigningKey;
@Value("${default.jwt.signingKey}")
private String jwtSigningKey;
@Autowired
private UserDetailsService userDetailsService;
......@@ -114,21 +114,28 @@ public class OAuth2ServerConfig {
@Override
public void configure(final HttpSecurity http) throws Exception {
/*@formatter:off*/
http
// Since we want the protected resources to be accessible in the UI as well we
// need
// session creation to be allowed (it's disabled by default in 2.0.6)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// cors
.and().cors()
// TODO 1.7 Delete "/token" URL
.and().requestMatchers().antMatchers("/api/**", "/google/verify-token", "/token")
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// TODO 1.7 Delete "/token" URL
.and().authorizeRequests().antMatchers("/api/**", "/google/verify-token", "/token").authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
// CORS
.and().cors()
// TODO 1.7 Delete "/token" URL
.and().requestMatchers()
.antMatchers("/api/**", "/google/verify-token", "/token")
// TODO 1.7 Delete "/token" URL
.and().authorizeRequests()
.antMatchers("/api/**", "/google/verify-token", "/token").authenticated()
.and().exceptionHandling()
.accessDeniedHandler(new OAuth2AccessDeniedHandler());
/*@formatter:on*/
}
@Override
......
......@@ -50,34 +50,59 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
return new BCryptPasswordEncoder();
}
// @Bean
// public AuthenticationProvider runAsAuthenticationProvider() {
// RunAsImplAuthenticationProvider authProvider = new RunAsImplAuthenticationProvider();
// authProvider.setKey("MyRunAsKey");
// return authProvider;
// }
// @Bean
// public AuthenticationProvider runAsAuthenticationProvider() {
// RunAsImplAuthenticationProvider authProvider = new
// RunAsImplAuthenticationProvider();
// authProvider.setKey("MyRunAsKey");
// return authProvider;
// }
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth
//.authenticationProvider(runAsAuthenticationProvider())
// .authenticationProvider(runAsAuthenticationProvider())
.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
/*@formatter:off*/
http
// No JSESSIONID in URL
.sessionManagement().enableSessionUrlRewriting(false) // Authorizations
.sessionManagement()
.enableSessionUrlRewriting(false)
// Anon
.and().anonymous()
.authorities("ROLE_ANONYMOUS", "ROLE_EVERYONE")
// Authorizations
.and().authorizeRequests()
// Rules
.antMatchers("/user/login").permitAll().antMatchers("/browse.jsp").permitAll().antMatchers("/").hasAnyRole("USER").and().exceptionHandling()
// access denied
.accessDeniedPage("/user/login?authorization_error=true").and().csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize")).disable()
.antMatchers("/user/login").permitAll()
.antMatchers("/browse.jsp").permitAll()
.antMatchers("/").hasAnyRole("USER")
// Error handling
.and().exceptionHandling()
// access denied
.accessDeniedPage("/user/login?authorization_error=true")
// CSRF
.and().csrf()
// Disabled on /oauth/authorize
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize")).disable()
// Logout and login
.logout().logoutUrl("/logout").logoutSuccessUrl("/")
.and().formLogin().permitAll().loginPage("/user/login").loginProcessingUrl("/login-attempt").failureUrl("/user/login?authentication_error=true");
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.and().formLogin()
.permitAll()
.loginPage("/user/login")
.loginProcessingUrl("/login-attempt")
.failureUrl("/user/login?authentication_error=true");
/*@formatter:on*/
}
@Override
......
......@@ -21,6 +21,7 @@ import java.util.UUID;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys.catalog.exceptions.InvalidApiUsageException;
import org.genesys.catalog.exceptions.NotFoundElement;
import org.genesys.filerepository.InvalidRepositoryFileDataException;
......@@ -33,6 +34,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
......@@ -60,14 +62,26 @@ public class RepositoryController {
@Autowired
protected RepositoryService repositoryService;
@Autowired
private CustomAclService aclService;
@PostMapping(value = "/add")
@Transactional
public RepositoryFile addFile(@RequestParam("file") final MultipartFile inputFile, @RequestParam("metadata") String metadataJson) throws NotFoundElement,
InvalidRepositoryFileDataException, InvalidRepositoryPathException, IOException {
RepositoryFile metadata = objectMapper.readValue(metadataJson, RepositoryFile.class);
String fileName = StringUtils.defaultString(metadata.getOriginalFilename(), inputFile.getOriginalFilename());
LOG.info("Uploading {} to repository", fileName);
return repositoryService.addFile(metadata.getPath(), fileName, inputFile.getContentType(), inputFile.getBytes(), metadata);
RepositoryFile repositoryFile = repositoryService.addFile(metadata.getPath(), fileName, inputFile.getContentType(), inputFile.getBytes(), metadata);
// Automatically public if in /content/ folder
if (metadata.getPath().startsWith("/content/")) {
aclService.makePubliclyReadable(repositoryFile, true);
}
return repositoryFile;
}
@GetMapping(value = "/{fileUuid}")
......
Supports Markdown
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