Skip to content
Snippets Groups Projects
Unverified Commit c50c4640 authored by Martin Weise's avatar Martin Weise
Browse files

Merge branch 'dev' into 242-use-mariadb-for-metadata-database-easy

parents c3e53873 7c036708
No related branches found
No related tags found
4 merge requests!129New module for citation as they occur multiple,!121Modified logging, modified logging level, modified flasgger endpoint,!113Resolve "Bugs related with Query Service",!109Resolve "Use MariaDB for metadata database"
Showing
with 378 additions and 74 deletions
...@@ -11,7 +11,7 @@ volumes: ...@@ -11,7 +11,7 @@ volumes:
networks: networks:
public: public:
name: core name: public
driver: bridge driver: bridge
ipam: ipam:
config: config:
...@@ -23,7 +23,7 @@ networks: ...@@ -23,7 +23,7 @@ networks:
config: config:
- subnet: 172.28.0.0/16 - subnet: 172.28.0.0/16
core: core:
name: public name: core
driver: bridge driver: bridge
ipam: ipam:
config: config:
...@@ -361,6 +361,7 @@ services: ...@@ -361,6 +361,7 @@ services:
image: fda-ui image: fda-ui
networks: networks:
core: core:
public:
ports: ports:
- "3000:3000" - "3000:3000"
env_file: env_file:
......
...@@ -81,16 +81,17 @@ public class TokenEndpoint { ...@@ -81,16 +81,17 @@ public class TokenEndpoint {
.body(dto); .body(dto);
} }
@DeleteMapping("/{hash}") @DeleteMapping("/{id}")
@Transactional @Transactional
@Timed(value = "token.delete", description = "Time needed to delete the developer tokens") @Timed(value = "token.delete", description = "Time needed to delete the developer tokens")
@Operation(summary = "Delete developer token", security = @SecurityRequirement(name = "bearerAuth")) @Operation(summary = "Delete developer token", security = @SecurityRequirement(name = "bearerAuth"))
public void delete(@NotNull @PathVariable("hash") String hash, public void delete(@NotNull @PathVariable("id") Long id,
@NotNull Principal principal) throws TokenNotFoundException, UserNotFoundException { @NotNull Principal principal) throws TokenNotFoundException, UserNotFoundException {
log.debug("endpoint delete developer token, hash={}, principal={}", hash, principal); log.debug("endpoint delete developer token, id={}, principal={}", id, principal);
final Token token = tokenService.findOne(hash); final Token token = tokenService.findOne(id);
log.trace("found token {}", token); log.trace("found token {}", token);
tokenService.delete(token.getTokenHash(), principal); tokenService.delete(token.getTokenHash(), principal);
log.info("Deleted token with id {}", id);
} }
} }
\ No newline at end of file
package at.tuwien; package at.tuwien;
import at.tuwien.api.auth.SignupRequestDto; import at.tuwien.api.user.UserDetailsDto;
import at.tuwien.entities.user.RoleType;
import at.tuwien.entities.user.TimeSecret; import at.tuwien.entities.user.TimeSecret;
import at.tuwien.entities.user.Token; import at.tuwien.entities.user.Token;
import at.tuwien.entities.user.User; import at.tuwien.entities.user.User;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import java.security.Principal;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.List;
@TestPropertySource(locations = "classpath:application.properties") @TestPropertySource(locations = "classpath:application.properties")
public abstract class BaseUnitTest { public abstract class BaseUnitTest {
...@@ -38,9 +44,21 @@ public abstract class BaseUnitTest { ...@@ -38,9 +44,21 @@ public abstract class BaseUnitTest {
.emailVerified(USER_1_VERIFIED) .emailVerified(USER_1_VERIFIED)
.themeDark(USER_1_THEME_DARK) .themeDark(USER_1_THEME_DARK)
.created(USER_1_CREATED) .created(USER_1_CREATED)
.roles(List.of(RoleType.ROLE_RESEARCHER))
.lastModified(USER_1_LAST_MODIFIED) .lastModified(USER_1_LAST_MODIFIED)
.build(); .build();
public final static UserDetails USER_1_DETAILS = UserDetailsDto.builder()
.username(USER_1_USERNAME)
.email(USER_1_EMAIL)
.password(USER_1_PASSWORD)
.authorities(List.of(new SimpleGrantedAuthority("ROLE_RESEARCHER")))
.build();
public final static Principal USER_1_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_1_DETAILS,
USER_1_PASSWORD, USER_1_DETAILS.getAuthorities());
public final static Long USER_2_ID = 2L; public final static Long USER_2_ID = 2L;
public final static String USER_2_EMAIL = "jane.doe@example.com"; public final static String USER_2_EMAIL = "jane.doe@example.com";
public final static String USER_2_USERNAME = "jdoe2"; public final static String USER_2_USERNAME = "jdoe2";
...@@ -59,37 +77,64 @@ public abstract class BaseUnitTest { ...@@ -59,37 +77,64 @@ public abstract class BaseUnitTest {
.emailVerified(USER_2_VERIFIED) .emailVerified(USER_2_VERIFIED)
.themeDark(USER_2_THEME_DARK) .themeDark(USER_2_THEME_DARK)
.created(USER_2_CREATED) .created(USER_2_CREATED)
.roles(List.of(RoleType.ROLE_RESEARCHER))
.lastModified(USER_2_LAST_MODIFIED) .lastModified(USER_2_LAST_MODIFIED)
.build(); .build();
public final static Long TOKEN_1_ID = 1L; public final static UserDetails USER_2_DETAILS = UserDetailsDto.builder()
public final static Boolean TOKEN_1_PROCESSED = false; .username(USER_2_USERNAME)
public final static String TOKEN_1_TOKEN = "mysecrettokenrandomlygenerated"; .email(USER_2_EMAIL)
public final static Instant TOKEN_1_VALID_TO = Instant.now() .password(USER_2_PASSWORD)
.authorities(List.of(new SimpleGrantedAuthority("ROLE_RESEARCHER")))
.build();
public final static Principal USER_2_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_2_DETAILS,
USER_2_PASSWORD, USER_2_DETAILS.getAuthorities());
public final static Long TIME_SECRET_1_ID = 1L;
public final static Boolean TIME_SECRET_1_PROCESSED = false;
public final static String TIME_SECRET_1_TOKEN = "mysecrettokenrandomlygenerated";
public final static Instant TIME_SECRET_1_VALID_TO = Instant.now()
.plus(1, ChronoUnit.DAYS); .plus(1, ChronoUnit.DAYS);
public final static Long TOKEN_2_ID = 2L; public final static Long TIME_SECRET_2_ID = 2L;
public final static Boolean TOKEN_2_PROCESSED = true; public final static Boolean TIME_SECRET_2_PROCESSED = true;
public final static String TOKEN_2_TOKEN = "blahblahblah"; public final static String TIME_SECRET_2_TOKEN = "blahblahblah";
public final static Instant TOKEN_2_VALID_TO = Instant.now() public final static Instant TIME_SECRET_2_VALID_TO = Instant.now()
.plus(1, ChronoUnit.DAYS); .plus(1, ChronoUnit.DAYS);
public final static TimeSecret TOKEN_1 = TimeSecret.builder() public final static TimeSecret TIME_SECRET_1 = TimeSecret.builder()
.id(TOKEN_1_ID) .id(TIME_SECRET_1_ID)
.uid(USER_1_ID) .uid(USER_1_ID)
.user(USER_1) .user(USER_1)
.token(TOKEN_1_TOKEN) .token(TIME_SECRET_1_TOKEN)
.processed(TOKEN_1_PROCESSED) .processed(TIME_SECRET_1_PROCESSED)
.validTo(TOKEN_1_VALID_TO) .validTo(TIME_SECRET_1_VALID_TO)
.build(); .build();
public final static TimeSecret TOKEN_2 = TimeSecret.builder() public final static TimeSecret TIME_SECRET_2 = TimeSecret.builder()
.id(TOKEN_2_ID) .id(TIME_SECRET_2_ID)
.uid(USER_2_ID) .uid(USER_2_ID)
.user(USER_2) .user(USER_2)
.token(TOKEN_2_TOKEN) .token(TIME_SECRET_2_TOKEN)
.processed(TOKEN_2_PROCESSED) .processed(TIME_SECRET_2_PROCESSED)
.validTo(TOKEN_2_VALID_TO) .validTo(TIME_SECRET_2_VALID_TO)
.build();
public final static Long TOKEN_1_ID = 1L;
public final static Instant TOKEN_1_EXPIRES = Instant.now().plus(100000000, ChronoUnit.MILLIS);
public final static Token TOKEN_1 = Token.builder()
.id(TOKEN_1_ID)
.expires(TOKEN_1_EXPIRES)
.build();
public final static Long TOKEN_2_ID = 2L;
public final static Instant TOKEN_2_EXPIRES = Instant.now().plus(100000000, ChronoUnit.MILLIS);
public final static Token TOKEN_2 = Token.builder()
.id(TOKEN_2_ID)
.expires(TOKEN_2_EXPIRES)
.build(); .build();
} }
package at.tuwien.config;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.codehaus.plexus.util.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import java.io.File;
import java.io.IOException;
@Log4j2
@Component
public class H2Utils {
@Autowired
private EntityManager entityManager;
@Transactional
public void runQuery(String query) {
log.debug("query={}", query);
entityManager.createNativeQuery(query)
.executeUpdate();
}
@Transactional
public void runScript(String scriptName) {
try {
runQuery(FileUtils.fileRead(new File("./src/test/resources/" + scriptName)));
} catch (IOException e) {
log.error("Failed to load script {}", scriptName);
throw new RuntimeException("Failed to load script", e);
}
}
}
...@@ -41,10 +41,10 @@ public class AuthenticationServiceIntegrationTest extends BaseUnitTest { ...@@ -41,10 +41,10 @@ public class AuthenticationServiceIntegrationTest extends BaseUnitTest {
public void beforeEach() { public void beforeEach() {
final User u1 = userRepository.save(USER_1); final User u1 = userRepository.save(USER_1);
final User u2 = userRepository.save(USER_2); final User u2 = userRepository.save(USER_2);
TOKEN_1.setUser(u1); TIME_SECRET_1.setUser(u1);
tokenRepository.save(TOKEN_1); tokenRepository.save(TIME_SECRET_1);
TOKEN_2.setUser(u2); TIME_SECRET_2.setUser(u2);
tokenRepository.save(TOKEN_2); tokenRepository.save(TIME_SECRET_2);
} }
@Test @Test
......
...@@ -21,16 +21,16 @@ import static org.junit.jupiter.api.Assertions.assertThrows; ...@@ -21,16 +21,16 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
@SpringBootTest @SpringBootTest
@ExtendWith(SpringExtension.class) @ExtendWith(SpringExtension.class)
public class TokenServiceIntegrationTest extends BaseUnitTest { public class TimeSecretUnitTest extends BaseUnitTest {
@MockBean @MockBean
private ReadyConfig readyConfig; private ReadyConfig readyConfig;
@Autowired @Autowired
private TimeSecretService tokenService; private TimeSecretService timeSecretService;
@Autowired @Autowired
private TimeSecretRepository tokenRepository; private TimeSecretRepository timeSecretRepository;
@Autowired @Autowired
private UserRepository userRepository; private UserRepository userRepository;
...@@ -38,16 +38,16 @@ public class TokenServiceIntegrationTest extends BaseUnitTest { ...@@ -38,16 +38,16 @@ public class TokenServiceIntegrationTest extends BaseUnitTest {
@BeforeEach @BeforeEach
public void beforeEach() { public void beforeEach() {
userRepository.save(USER_1); userRepository.save(USER_1);
tokenRepository.save(TOKEN_1); timeSecretRepository.save(TIME_SECRET_1);
} }
@Test @Test
public void updateVerification_succeeds() throws SecretInvalidException { public void updateVerification_succeeds() throws SecretInvalidException {
/* test */ /* test */
tokenService.invalidate(TOKEN_1_TOKEN); timeSecretService.invalidate(TIME_SECRET_1_TOKEN);
assertThrows(SecretInvalidException.class, () -> { assertThrows(SecretInvalidException.class, () -> {
tokenService.invalidate(TOKEN_1_TOKEN); timeSecretService.invalidate(TIME_SECRET_1_TOKEN);
}); });
} }
......
package at.tuwien.service;
import at.tuwien.BaseUnitTest;
import at.tuwien.auth.JwtUtils;
import at.tuwien.config.H2Utils;
import at.tuwien.config.ReadyConfig;
import at.tuwien.entities.user.Token;
import at.tuwien.exception.UserNotFoundException;
import at.tuwien.repositories.TokenRepository;
import at.tuwien.repositories.UserRepository;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.servlet.ServletException;
import static org.junit.jupiter.api.Assertions.assertThrows;
@Log4j2
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
@SpringBootTest
@ExtendWith(SpringExtension.class)
public class TokenIntegrationTest extends BaseUnitTest {
@MockBean
private ReadyConfig readyConfig;
@Autowired
private TokenService tokenService;
@Autowired
private UserRepository userRepository;
@Autowired
private TokenRepository tokenRepository;
@Autowired
private JwtUtils jwtUtils;
@Autowired
private H2Utils h2Utils;
@BeforeEach
public void beforeEach() {
userRepository.save(USER_1);
h2Utils.runScript("post-init.sql");
}
@Test
public void check_succeeds() throws ServletException {
/* mock */
final String jwt = jwtUtils.generateJwtToken(USER_1_USERNAME, TOKEN_1_EXPIRES);
final Token entity = Token.builder()
.token(jwt)
.tokenHash(JwtUtils.toHash(jwt))
.creator(USER_1_ID)
.expires(TOKEN_1_EXPIRES)
.build();
tokenRepository.save(entity) /* mock as invalid by the view script in ./resources/post-init.sql */;
final String jwt2 = jwtUtils.generateJwtToken(USER_1_USERNAME, TOKEN_2_EXPIRES);
final Token entity2 = Token.builder()
.token(jwt2)
.tokenHash(JwtUtils.toHash(jwt2))
.creator(USER_1_ID)
.expires(TOKEN_2_EXPIRES)
.build();
tokenRepository.save(entity2);
/* test */
tokenService.check(jwt2);
}
@Test
public void check_revoked_fails() {
/* mock */
final String jwt = jwtUtils.generateJwtToken(USER_1_USERNAME, TOKEN_1_EXPIRES);
final Token entity = Token.builder()
.token(jwt)
.tokenHash(JwtUtils.toHash(jwt))
.creator(USER_1_ID)
.expires(TOKEN_1_EXPIRES)
.build();
final Token token = tokenRepository.save(entity);
tokenRepository.deleteById(token.getId());
/* test */
assertThrows(ServletException.class, () -> {
tokenService.check(jwt);
});
}
@Test
public void create_userNotFound_fails() {
/* test */
assertThrows(UserNotFoundException.class, () -> {
tokenService.create(USER_2_PRINCIPAL);
});
}
}
...@@ -9,7 +9,7 @@ spring.cloud.config.discovery.enabled = false ...@@ -9,7 +9,7 @@ spring.cloud.config.discovery.enabled = false
spring.cloud.config.enabled = false spring.cloud.config.enabled = false
# disable datasource # disable datasource
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS FDA spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE;INIT=runscript from './src/test/resources/pre-init.sql'
spring.datasource.driverClassName=org.h2.Driver spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa spring.datasource.username=sa
spring.datasource.password=password spring.datasource.password=password
......
-- Modified for H2
-- Assume id=1 is invalid
-- Assume id=2 is still valid token
CREATE VIEW IF NOT EXISTS mdb_valid_tokens AS
(
SELECT `id`, `token_hash`, `creator`, `created`, `expires`, `last_used`
FROM (SELECT `id`, `token_hash`, `creator`, `created`, `expires`, `last_used` FROM FDA.`mdb_tokens`)
WHERE `id` != 1 GROUP BY `id`);
\ No newline at end of file
CREATE SCHEMA IF NOT EXISTS FDA;
\ No newline at end of file
package at.tuwien.auth; package at.tuwien.auth;
import at.tuwien.service.TokenService;
import at.tuwien.service.impl.UserDetailsServiceImpl; import at.tuwien.service.impl.UserDetailsServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
...@@ -20,10 +21,12 @@ import java.io.IOException; ...@@ -20,10 +21,12 @@ import java.io.IOException;
public class AuthTokenFilter extends OncePerRequestFilter { public class AuthTokenFilter extends OncePerRequestFilter {
private final JwtUtils jwtUtils; private final JwtUtils jwtUtils;
private final TokenService tokenService;
private final UserDetailsServiceImpl userDetailsService; private final UserDetailsServiceImpl userDetailsService;
public AuthTokenFilter(JwtUtils jwtUtils, UserDetailsServiceImpl userDetailsService) { public AuthTokenFilter(JwtUtils jwtUtils, TokenService tokenService, UserDetailsServiceImpl userDetailsService) {
this.jwtUtils = jwtUtils; this.jwtUtils = jwtUtils;
this.tokenService = tokenService;
this.userDetailsService = userDetailsService; this.userDetailsService = userDetailsService;
} }
...@@ -32,6 +35,7 @@ public class AuthTokenFilter extends OncePerRequestFilter { ...@@ -32,6 +35,7 @@ public class AuthTokenFilter extends OncePerRequestFilter {
throws ServletException, IOException { throws ServletException, IOException {
final String jwt = parseJwt(request); final String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) { if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
tokenService.check(jwt);
final String username = jwtUtils.getUserNameFromJwtToken(jwt); final String username = jwtUtils.getUserNameFromJwtToken(jwt);
final UserDetails userDetails; final UserDetails userDetails;
try { try {
......
...@@ -5,6 +5,7 @@ import com.auth0.jwt.algorithms.Algorithm; ...@@ -5,6 +5,7 @@ import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
...@@ -40,6 +41,10 @@ public class JwtUtils { ...@@ -40,6 +41,10 @@ public class JwtUtils {
.getSubject(); .getSubject();
} }
public static String toHash(String token) {
return DigestUtils.sha256Hex(token);
}
public boolean validateJwtToken(String authToken) { public boolean validateJwtToken(String authToken) {
try { try {
final DecodedJWT jwt = JWT.decode(authToken); final DecodedJWT jwt = JWT.decode(authToken);
......
...@@ -2,6 +2,7 @@ package at.tuwien.config; ...@@ -2,6 +2,7 @@ package at.tuwien.config;
import at.tuwien.auth.AuthTokenFilter; import at.tuwien.auth.AuthTokenFilter;
import at.tuwien.auth.JwtUtils; import at.tuwien.auth.JwtUtils;
import at.tuwien.service.TokenService;
import at.tuwien.service.impl.UserDetailsServiceImpl; import at.tuwien.service.impl.UserDetailsServiceImpl;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityScheme; import io.swagger.v3.oas.annotations.security.SecurityScheme;
...@@ -35,20 +36,22 @@ import javax.servlet.http.HttpServletResponse; ...@@ -35,20 +36,22 @@ import javax.servlet.http.HttpServletResponse;
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtUtils jwtUtils; private final JwtUtils jwtUtils;
private final TokenService tokenService;
private final SecurityConfig securityConfig; private final SecurityConfig securityConfig;
private final UserDetailsServiceImpl userDetailsService; private final UserDetailsServiceImpl userDetailsService;
@Autowired @Autowired
public WebSecurityConfig(JwtUtils jwtUtils, SecurityConfig securityConfig, public WebSecurityConfig(JwtUtils jwtUtils, TokenService tokenService, SecurityConfig securityConfig,
UserDetailsServiceImpl userDetailsService) { UserDetailsServiceImpl userDetailsService) {
this.jwtUtils = jwtUtils; this.jwtUtils = jwtUtils;
this.tokenService = tokenService;
this.securityConfig = securityConfig; this.securityConfig = securityConfig;
this.userDetailsService = userDetailsService; this.userDetailsService = userDetailsService;
} }
@Bean @Bean
public AuthTokenFilter authTokenFilter() { public AuthTokenFilter authTokenFilter() {
return new AuthTokenFilter(jwtUtils, userDetailsService); return new AuthTokenFilter(jwtUtils, tokenService, userDetailsService);
} }
@Override @Override
......
...@@ -17,4 +17,7 @@ public interface TokenRepository extends JpaRepository<Token, Long> { ...@@ -17,4 +17,7 @@ public interface TokenRepository extends JpaRepository<Token, Long> {
Optional<Token> findByTokenHash(String tokenHash); Optional<Token> findByTokenHash(String tokenHash);
Optional<Token> findByValidTokenHash(@Param("hash") String tokenHash);
} }
...@@ -5,7 +5,9 @@ import at.tuwien.exception.TokenNotEligableException; ...@@ -5,7 +5,9 @@ import at.tuwien.exception.TokenNotEligableException;
import at.tuwien.exception.TokenNotFoundException; import at.tuwien.exception.TokenNotFoundException;
import at.tuwien.exception.UserNotFoundException; import at.tuwien.exception.UserNotFoundException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.ServletException;
import java.security.Principal; import java.security.Principal;
import java.util.List; import java.util.List;
...@@ -40,6 +42,15 @@ public interface TokenService { ...@@ -40,6 +42,15 @@ public interface TokenService {
*/ */
Token findOne(String tokenHash) throws TokenNotFoundException; Token findOne(String tokenHash) throws TokenNotFoundException;
/**
* Finds a token by id.
*
* @param id The token id.
* @return The token, if successful.
* @throws TokenNotFoundException The token was not found in the metadata database.
*/
Token findOne(Long id) throws TokenNotFoundException;
/** /**
* Deletes a developer token in the metadata database by hash and user principal. * Deletes a developer token in the metadata database by hash and user principal.
* *
...@@ -49,4 +60,11 @@ public interface TokenService { ...@@ -49,4 +60,11 @@ public interface TokenService {
* @throws UserNotFoundException The user does not exist in the metadata database. * @throws UserNotFoundException The user does not exist in the metadata database.
*/ */
void delete(String tokenHash, Principal principal) throws TokenNotFoundException, UserNotFoundException; void delete(String tokenHash, Principal principal) throws TokenNotFoundException, UserNotFoundException;
/**
* Checks if the developer token has not been marked as deleted
*
* @param jwt The token
*/
void check(String jwt) throws ServletException;
} }
...@@ -15,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -15,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.servlet.ServletException;
import java.security.Principal; import java.security.Principal;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
...@@ -80,6 +81,17 @@ public class TokenServiceImpl implements TokenService { ...@@ -80,6 +81,17 @@ public class TokenServiceImpl implements TokenService {
return optional.get(); return optional.get();
} }
@Override
@Transactional(readOnly = true)
public Token findOne(Long id) throws TokenNotFoundException {
final Optional<Token> optional = tokenRepository.findById(id);
if (optional.isEmpty()) {
log.error("Failed to find token with id {}", id);
throw new TokenNotFoundException("Failed to find token");
}
return optional.get();
}
@Override @Override
@Transactional @Transactional
public void delete(String tokenHash, Principal principal) throws TokenNotFoundException, UserNotFoundException { public void delete(String tokenHash, Principal principal) throws TokenNotFoundException, UserNotFoundException {
...@@ -94,4 +106,18 @@ public class TokenServiceImpl implements TokenService { ...@@ -94,4 +106,18 @@ public class TokenServiceImpl implements TokenService {
log.debug("deleted token {}", token); log.debug("deleted token {}", token);
} }
@Override
@Transactional
public void check(String jwt) throws ServletException {
final String tokenHash = JwtUtils.toHash(jwt);
final Optional<Token> optional = tokenRepository.findByValidTokenHash(tokenHash);
if (optional.isEmpty()) {
log.error("Token with hash {} is marked as revoked", tokenHash);
throw new ServletException("Token with hash " + tokenHash + " is marked as revoked");
}
final Token token = optional.get();
token.setLastUsed(Instant.now());
tokenRepository.save(token);
}
} }
...@@ -3,7 +3,6 @@ package at.tuwien.entities.user; ...@@ -3,7 +3,6 @@ package at.tuwien.entities.user;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.SQLDelete;
import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener; import org.springframework.data.jpa.domain.support.AuditingEntityListener;
...@@ -19,6 +18,11 @@ import java.time.Instant; ...@@ -19,6 +18,11 @@ import java.time.Instant;
@EntityListeners(AuditingEntityListener.class) @EntityListeners(AuditingEntityListener.class)
@EqualsAndHashCode(onlyExplicitlyIncluded = true) @EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Table(name = "mdb_tokens") @Table(name = "mdb_tokens")
@NamedNativeQueries({
@NamedNativeQuery(name = "Token.findByValidTokenHash",
query = "SELECT * FROM `mdb_valid_tokens` WHERE `token_hash` = :hash",
resultClass = Token.class)
})
public class Token { public class Token {
@Id @Id
...@@ -46,7 +50,7 @@ public class Token { ...@@ -46,7 +50,7 @@ public class Token {
@Column(nullable = false, updatable = false) @Column(nullable = false, updatable = false)
private Instant expires; private Instant expires;
@Column(nullable = false, updatable = false) @Column
private Instant lastUsed; private Instant lastUsed;
} }
...@@ -23,7 +23,7 @@ CREATE TABLE IF NOT EXISTS mdb_users ...@@ -23,7 +23,7 @@ CREATE TABLE IF NOT EXISTS mdb_users
UNIQUE (username), UNIQUE (username),
UNIQUE (Main_Email), UNIQUE (Main_Email),
UNIQUE (OID) UNIQUE (OID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE mdb_images CREATE TABLE mdb_images
( (
...@@ -41,7 +41,7 @@ CREATE TABLE mdb_images ...@@ -41,7 +41,7 @@ CREATE TABLE mdb_images
last_modified timestamp, last_modified timestamp,
PRIMARY KEY (id), PRIMARY KEY (id),
UNIQUE (repository, tag) UNIQUE (repository, tag)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE mdb_time_secrets CREATE TABLE mdb_time_secrets
( (
...@@ -53,7 +53,7 @@ CREATE TABLE mdb_time_secrets ...@@ -53,7 +53,7 @@ CREATE TABLE mdb_time_secrets
valid_to timestamp NOT NULL, valid_to timestamp NOT NULL,
PRIMARY KEY (id), PRIMARY KEY (id),
FOREIGN KEY (uid) REFERENCES mdb_users (UserID) FOREIGN KEY (uid) REFERENCES mdb_users (UserID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE mdb_tokens CREATE TABLE mdb_tokens
( (
...@@ -65,7 +65,7 @@ CREATE TABLE mdb_tokens ...@@ -65,7 +65,7 @@ CREATE TABLE mdb_tokens
last_used timestamp, last_used timestamp,
PRIMARY KEY (id), PRIMARY KEY (id),
FOREIGN KEY (creator) REFERENCES mdb_users (UserID) FOREIGN KEY (creator) REFERENCES mdb_users (UserID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE mdb_images_date CREATE TABLE mdb_images_date
( (
...@@ -79,7 +79,7 @@ CREATE TABLE mdb_images_date ...@@ -79,7 +79,7 @@ CREATE TABLE mdb_images_date
PRIMARY KEY (id), PRIMARY KEY (id),
FOREIGN KEY (iid) REFERENCES mdb_images (id), FOREIGN KEY (iid) REFERENCES mdb_images (id),
UNIQUE (database_format, unix_format, example) UNIQUE (database_format, unix_format, example)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_containers CREATE TABLE IF NOT EXISTS mdb_containers
( (
...@@ -96,7 +96,7 @@ CREATE TABLE IF NOT EXISTS mdb_containers ...@@ -96,7 +96,7 @@ CREATE TABLE IF NOT EXISTS mdb_containers
PRIMARY KEY (id), PRIMARY KEY (id),
FOREIGN KEY (created_by) REFERENCES mdb_users (UserID), FOREIGN KEY (created_by) REFERENCES mdb_users (UserID),
FOREIGN KEY (image_id) REFERENCES mdb_images (id) FOREIGN KEY (image_id) REFERENCES mdb_images (id)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE mdb_images_environment_item CREATE TABLE mdb_images_environment_item
( (
...@@ -109,7 +109,7 @@ CREATE TABLE mdb_images_environment_item ...@@ -109,7 +109,7 @@ CREATE TABLE mdb_images_environment_item
last_modified timestamp, last_modified timestamp,
PRIMARY KEY (id, iid), PRIMARY KEY (id, iid),
FOREIGN KEY (iid) REFERENCES mdb_images (id) FOREIGN KEY (iid) REFERENCES mdb_images (id)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_data CREATE TABLE IF NOT EXISTS mdb_data
( (
...@@ -120,7 +120,7 @@ CREATE TABLE IF NOT EXISTS mdb_data ...@@ -120,7 +120,7 @@ CREATE TABLE IF NOT EXISTS mdb_data
Version TEXT, Version TEXT,
Seperator TEXT, Seperator TEXT,
PRIMARY KEY (ID) PRIMARY KEY (ID)
); ) WITH SYSTEM VERSIONING
CREATE TABLE IF NOT EXISTS mdb_user_roles CREATE TABLE IF NOT EXISTS mdb_user_roles
( (
...@@ -131,7 +131,7 @@ CREATE TABLE IF NOT EXISTS mdb_user_roles ...@@ -131,7 +131,7 @@ CREATE TABLE IF NOT EXISTS mdb_user_roles
PRIMARY KEY (uid), PRIMARY KEY (uid),
FOREIGN KEY (uid) REFERENCES mdb_users (UserID), FOREIGN KEY (uid) REFERENCES mdb_users (UserID),
UNIQUE (uid, role) UNIQUE (uid, role)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_licenses CREATE TABLE IF NOT EXISTS mdb_licenses
( (
...@@ -139,7 +139,7 @@ CREATE TABLE IF NOT EXISTS mdb_licenses ...@@ -139,7 +139,7 @@ CREATE TABLE IF NOT EXISTS mdb_licenses
uri TEXT NOT NULL, uri TEXT NOT NULL,
PRIMARY KEY (identifier), PRIMARY KEY (identifier),
UNIQUE (uri) UNIQUE (uri)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_databases CREATE TABLE IF NOT EXISTS mdb_databases
( (
...@@ -158,14 +158,14 @@ CREATE TABLE IF NOT EXISTS mdb_databases ...@@ -158,14 +158,14 @@ CREATE TABLE IF NOT EXISTS mdb_databases
FOREIGN KEY (created_by) REFERENCES mdb_users (UserID), FOREIGN KEY (created_by) REFERENCES mdb_users (UserID),
FOREIGN KEY (contact_person) REFERENCES mdb_users (UserID), FOREIGN KEY (contact_person) REFERENCES mdb_users (UserID),
FOREIGN KEY (id) REFERENCES mdb_containers (id) /* currently we only support one-to-one */ FOREIGN KEY (id) REFERENCES mdb_containers (id) /* currently we only support one-to-one */
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_databases_subjects CREATE TABLE IF NOT EXISTS mdb_databases_subjects
( (
dbid BIGINT NOT NULL, dbid BIGINT NOT NULL,
subjects character varying(255) NOT NULL, subjects character varying(255) NOT NULL,
PRIMARY KEY (dbid, subjects) PRIMARY KEY (dbid, subjects)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_tables CREATE TABLE IF NOT EXISTS mdb_tables
( (
...@@ -190,7 +190,7 @@ CREATE TABLE IF NOT EXISTS mdb_tables ...@@ -190,7 +190,7 @@ CREATE TABLE IF NOT EXISTS mdb_tables
PRIMARY KEY (ID, tDBID), PRIMARY KEY (ID, tDBID),
FOREIGN KEY (created_by) REFERENCES mdb_users (UserID), FOREIGN KEY (created_by) REFERENCES mdb_users (UserID),
FOREIGN KEY (tDBID) REFERENCES mdb_databases (id) FOREIGN KEY (tDBID) REFERENCES mdb_databases (id)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_columns CREATE TABLE IF NOT EXISTS mdb_columns
( (
...@@ -215,7 +215,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns ...@@ -215,7 +215,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns
FOREIGN KEY (cDBID, tID) REFERENCES mdb_tables (tDBID, ID), FOREIGN KEY (cDBID, tID) REFERENCES mdb_tables (tDBID, ID),
FOREIGN KEY (created_by) REFERENCES mdb_users (UserID), FOREIGN KEY (created_by) REFERENCES mdb_users (UserID),
PRIMARY KEY (ID, cDBID, tID) PRIMARY KEY (ID, cDBID, tID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_columns_enums CREATE TABLE IF NOT EXISTS mdb_columns_enums
( (
...@@ -228,7 +228,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns_enums ...@@ -228,7 +228,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns_enums
last_modified timestamp, last_modified timestamp,
FOREIGN KEY (eDBID, tID, cID) REFERENCES mdb_columns (cDBID, tID, ID), FOREIGN KEY (eDBID, tID, cID) REFERENCES mdb_columns (cDBID, tID, ID),
PRIMARY KEY (ID, eDBID, tID, cID) PRIMARY KEY (ID, eDBID, tID, cID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_columns_nom CREATE TABLE IF NOT EXISTS mdb_columns_nom
( (
...@@ -240,7 +240,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns_nom ...@@ -240,7 +240,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns_nom
created timestamp NOT NULL DEFAULT NOW(), created timestamp NOT NULL DEFAULT NOW(),
FOREIGN KEY (cDBID, tID, cID) REFERENCES mdb_columns (cDBID, tID, ID), FOREIGN KEY (cDBID, tID, cID) REFERENCES mdb_columns (cDBID, tID, ID),
PRIMARY KEY (cDBID, tID, cID) PRIMARY KEY (cDBID, tID, cID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_columns_num CREATE TABLE IF NOT EXISTS mdb_columns_num
( (
...@@ -258,7 +258,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns_num ...@@ -258,7 +258,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns_num
created timestamp NOT NULL DEFAULT NOW(), created timestamp NOT NULL DEFAULT NOW(),
FOREIGN KEY (cDBID, tID, cID) REFERENCES mdb_columns (cDBID, tID, ID), FOREIGN KEY (cDBID, tID, cID) REFERENCES mdb_columns (cDBID, tID, ID),
PRIMARY KEY (cDBID, tID, cID) PRIMARY KEY (cDBID, tID, cID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_columns_cat CREATE TABLE IF NOT EXISTS mdb_columns_cat
( (
...@@ -271,7 +271,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns_cat ...@@ -271,7 +271,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns_cat
created timestamp NOT NULL DEFAULT NOW(), created timestamp NOT NULL DEFAULT NOW(),
FOREIGN KEY (cDBID, tID, cID) REFERENCES mdb_columns (cDBID, tID, ID), FOREIGN KEY (cDBID, tID, cID) REFERENCES mdb_columns (cDBID, tID, ID),
PRIMARY KEY (cDBID, tID, cID) PRIMARY KEY (cDBID, tID, cID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_concepts CREATE TABLE IF NOT EXISTS mdb_concepts
( (
...@@ -282,7 +282,7 @@ CREATE TABLE IF NOT EXISTS mdb_concepts ...@@ -282,7 +282,7 @@ CREATE TABLE IF NOT EXISTS mdb_concepts
created_by bigint, created_by bigint,
FOREIGN KEY (created_by) REFERENCES mdb_users (UserID), FOREIGN KEY (created_by) REFERENCES mdb_users (UserID),
PRIMARY KEY (id) PRIMARY KEY (id)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_columns_concepts CREATE TABLE IF NOT EXISTS mdb_columns_concepts
( (
...@@ -295,7 +295,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns_concepts ...@@ -295,7 +295,7 @@ CREATE TABLE IF NOT EXISTS mdb_columns_concepts
FOREIGN KEY (cDBID, tID, cID) REFERENCES mdb_columns (cDBID, tID, ID), FOREIGN KEY (cDBID, tID, cID) REFERENCES mdb_columns (cDBID, tID, ID),
FOREIGN KEY (concept_id) REFERENCES mdb_concepts (id), FOREIGN KEY (concept_id) REFERENCES mdb_concepts (id),
PRIMARY KEY (cDBID, tID, cID) PRIMARY KEY (cDBID, tID, cID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_view CREATE TABLE IF NOT EXISTS mdb_view
( (
...@@ -315,7 +315,7 @@ CREATE TABLE IF NOT EXISTS mdb_view ...@@ -315,7 +315,7 @@ CREATE TABLE IF NOT EXISTS mdb_view
FOREIGN KEY (created_by) REFERENCES mdb_users (UserID), FOREIGN KEY (created_by) REFERENCES mdb_users (UserID),
FOREIGN KEY (vdbid) REFERENCES mdb_databases (id), FOREIGN KEY (vdbid) REFERENCES mdb_databases (id),
PRIMARY KEY (id, vdbid) PRIMARY KEY (id, vdbid)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_identifiers CREATE TABLE IF NOT EXISTS mdb_identifiers
( (
...@@ -348,7 +348,7 @@ CREATE TABLE IF NOT EXISTS mdb_identifiers ...@@ -348,7 +348,7 @@ CREATE TABLE IF NOT EXISTS mdb_identifiers
FOREIGN KEY (dbid) REFERENCES mdb_databases (id), FOREIGN KEY (dbid) REFERENCES mdb_databases (id),
FOREIGN KEY (created_by) REFERENCES mdb_users (UserID), FOREIGN KEY (created_by) REFERENCES mdb_users (UserID),
UNIQUE (cid, dbid, qid) UNIQUE (cid, dbid, qid)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_related_identifiers CREATE TABLE IF NOT EXISTS mdb_related_identifiers
( (
...@@ -363,7 +363,7 @@ CREATE TABLE IF NOT EXISTS mdb_related_identifiers ...@@ -363,7 +363,7 @@ CREATE TABLE IF NOT EXISTS mdb_related_identifiers
PRIMARY KEY (id, iid), /* must be a single id from persistent identifier concept */ PRIMARY KEY (id, iid), /* must be a single id from persistent identifier concept */
FOREIGN KEY (iid) REFERENCES mdb_identifiers (id), FOREIGN KEY (iid) REFERENCES mdb_identifiers (id),
FOREIGN KEY (created_by) REFERENCES mdb_users (UserID) FOREIGN KEY (created_by) REFERENCES mdb_users (UserID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_creators CREATE TABLE IF NOT EXISTS mdb_creators
( (
...@@ -378,7 +378,7 @@ CREATE TABLE IF NOT EXISTS mdb_creators ...@@ -378,7 +378,7 @@ CREATE TABLE IF NOT EXISTS mdb_creators
FOREIGN KEY (created_by) REFERENCES mdb_users (UserID), FOREIGN KEY (created_by) REFERENCES mdb_users (UserID),
PRIMARY KEY (id, pid), PRIMARY KEY (id, pid),
FOREIGN KEY (pid) REFERENCES mdb_identifiers (id) FOREIGN KEY (pid) REFERENCES mdb_identifiers (id)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_feed CREATE TABLE IF NOT EXISTS mdb_feed
( (
...@@ -389,7 +389,7 @@ CREATE TABLE IF NOT EXISTS mdb_feed ...@@ -389,7 +389,7 @@ CREATE TABLE IF NOT EXISTS mdb_feed
created timestamp NOT NULL DEFAULT NOW(), created timestamp NOT NULL DEFAULT NOW(),
FOREIGN KEY (fDBID, fID) REFERENCES mdb_tables (tDBID, ID), FOREIGN KEY (fDBID, fID) REFERENCES mdb_tables (tDBID, ID),
PRIMARY KEY (fDBID, fID, fUserId, fDataID) PRIMARY KEY (fDBID, fID, fUserId, fDataID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_update CREATE TABLE IF NOT EXISTS mdb_update
( (
...@@ -397,7 +397,7 @@ CREATE TABLE IF NOT EXISTS mdb_update ...@@ -397,7 +397,7 @@ CREATE TABLE IF NOT EXISTS mdb_update
uDBID bigint REFERENCES mdb_databases (id), uDBID bigint REFERENCES mdb_databases (id),
created timestamp NOT NULL DEFAULT NOW(), created timestamp NOT NULL DEFAULT NOW(),
PRIMARY KEY (uUserID, uDBID) PRIMARY KEY (uUserID, uDBID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_access CREATE TABLE IF NOT EXISTS mdb_access
( (
...@@ -407,7 +407,7 @@ CREATE TABLE IF NOT EXISTS mdb_access ...@@ -407,7 +407,7 @@ CREATE TABLE IF NOT EXISTS mdb_access
download BOOLEAN, download BOOLEAN,
created timestamp NOT NULL DEFAULT NOW(), created timestamp NOT NULL DEFAULT NOW(),
PRIMARY KEY (aUserID, aDBID) PRIMARY KEY (aUserID, aDBID)
); ) WITH SYSTEM VERSIONING
CREATE TABLE IF NOT EXISTS mdb_have_access CREATE TABLE IF NOT EXISTS mdb_have_access
( (
...@@ -416,7 +416,7 @@ CREATE TABLE IF NOT EXISTS mdb_have_access ...@@ -416,7 +416,7 @@ CREATE TABLE IF NOT EXISTS mdb_have_access
hType ENUM ('R', 'W'), hType ENUM ('R', 'W'),
created timestamp NOT NULL DEFAULT NOW(), created timestamp NOT NULL DEFAULT NOW(),
PRIMARY KEY (hUserID, hDBID) PRIMARY KEY (hUserID, hDBID)
); ) WITH SYSTEM VERSIONING;
CREATE TABLE IF NOT EXISTS mdb_owns CREATE TABLE IF NOT EXISTS mdb_owns
( (
...@@ -424,7 +424,18 @@ CREATE TABLE IF NOT EXISTS mdb_owns ...@@ -424,7 +424,18 @@ CREATE TABLE IF NOT EXISTS mdb_owns
oDBID bigint REFERENCES mdb_databases (ID), oDBID bigint REFERENCES mdb_databases (ID),
created timestamp NOT NULL DEFAULT NOW(), created timestamp NOT NULL DEFAULT NOW(),
PRIMARY KEY (oUserID, oDBID) PRIMARY KEY (oUserID, oDBID)
); ) WITH SYSTEM VERSIONING;
CREATE VIEW IF NOT EXISTS mdb_valid_tokens AS
(
SELECT `id`, `token_hash`, `creator`, `created`, `expires`, `last_used`
FROM (SELECT `id`, `token_hash`, `creator`, `created`, `expires`, `last_used`
FROM `mdb_tokens` FOR SYSTEM_TIME ALL) as t
WHERE NOT EXISTS(SELECT `token_hash`
FROM mdb_tokens AS tt
WHERE ROW_END > NOW()
AND tt.`token_hash` = t.`token_hash`)
GROUP BY `id`);
COMMIT; COMMIT;
......
package at.tuwien.endpoint; package at.tuwien.endpoint;
import at.tuwien.SortType; import at.tuwien.SortType;
import at.tuwien.api.database.query.ExecuteStatementDto;
import at.tuwien.entities.database.Database; import at.tuwien.entities.database.Database;
import at.tuwien.entities.database.table.Table; import at.tuwien.entities.database.table.Table;
import at.tuwien.entities.identifier.Identifier; import at.tuwien.entities.identifier.Identifier;
import at.tuwien.exception.DatabaseNotFoundException; import at.tuwien.exception.*;
import at.tuwien.exception.IdentifierNotFoundException;
import at.tuwien.exception.PaginationException;
import at.tuwien.exception.SortException;
import at.tuwien.service.DatabaseService; import at.tuwien.service.DatabaseService;
import at.tuwien.service.IdentifierService; import at.tuwien.service.IdentifierService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.Principal; import java.security.Principal;
import java.util.List; import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static at.tuwien.entities.identifier.VisibilityType.EVERYONE; import static at.tuwien.entities.identifier.VisibilityType.EVERYONE;
...@@ -99,6 +103,25 @@ public abstract class AbstractEndpoint { ...@@ -99,6 +103,25 @@ public abstract class AbstractEndpoint {
} }
} }
protected void validateForbiddenStatements(ExecuteStatementDto data) throws QueryMalformedException,
QueryStoreException {
final StringBuilder regex = new StringBuilder("[");
try {
FileUtils.readLines(new File("src/main/resources/forbidden.txt"), Charset.defaultCharset())
.forEach(regex::append);
} catch (IOException e) {
log.error("Failed to load forbidden keywords list, reason {}", e.getMessage());
throw new QueryStoreException("Failed to load forbidden keywords list", e);
}
final Pattern pattern = Pattern.compile(regex + "]");
final Matcher matcher = pattern.matcher(data.getStatement());
final boolean found = matcher.find();
if (found) {
log.error("Query contains blacklisted character");
throw new QueryMalformedException("Query contains blacklisted character");
}
}
protected Boolean hasQueuePermission(Long containerId, Long databaseId, Long tableId, String permissionCode, protected Boolean hasQueuePermission(Long containerId, Long databaseId, Long tableId, String permissionCode,
Principal principal) { Principal principal) {
log.trace("validate queue permission, containerId={}, databaseId={}, tableId={}, permissionCode={}, principal={}", log.trace("validate queue permission, containerId={}, databaseId={}, tableId={}, permissionCode={}, principal={}",
......
...@@ -20,6 +20,8 @@ import org.springframework.web.bind.annotation.*; ...@@ -20,6 +20,8 @@ import org.springframework.web.bind.annotation.*;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.security.Principal; import java.security.Principal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Log4j2 @Log4j2
@RestController @RestController
...@@ -63,6 +65,7 @@ public class QueryEndpoint extends AbstractEndpoint { ...@@ -63,6 +65,7 @@ public class QueryEndpoint extends AbstractEndpoint {
log.error("Failed to execute query: is empty"); log.error("Failed to execute query: is empty");
throw new QueryMalformedException("Failed to execute query"); throw new QueryMalformedException("Failed to execute query");
} }
validateForbiddenStatements(data);
validateDataParams(page, size, sortDirection, sortColumn); validateDataParams(page, size, sortDirection, sortColumn);
/* execute */ /* execute */
final QueryResultDto result = queryService.execute(containerId, databaseId, data, QueryTypeDto.QUERY, final QueryResultDto result = queryService.execute(containerId, databaseId, data, QueryTypeDto.QUERY,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment