Skip to content

Commit

Permalink
Merge pull request #575 from kdhrubo/feature/574_auth
Browse files Browse the repository at this point in the history
Feature/574 auth
  • Loading branch information
kdhrubo authored May 17, 2024
2 parents 51e5b48 + 494a7a3 commit 18701e7
Show file tree
Hide file tree
Showing 29 changed files with 300 additions and 169 deletions.
8 changes: 8 additions & 0 deletions api-rest/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
</distributionManagement>

<dependencies>

<dependency>
<groupId>com.github</groupId>
<artifactId>auth</artifactId>
<version>1.0.0-RC1</version>
</dependency>


<dependency>
<groupId>com.github</groupId>
<artifactId>rest-common</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion api-rest/src/main/resources/application-db.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ app:
schemas:
connectionProperties:
ssl: false
envProperties:
envProperties: # Not used yet
enableDatetimeFormatting: ${ENABLE_DATETIME_FORMATTING:false}
timeFormat: ${TIME_FORMAT:HH:mm:ss}
dateFormat: ${DATE_FORMAT:yyyy-MM-dd}
Expand Down
2 changes: 1 addition & 1 deletion api-rest/src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ app:
password: homi2022
connectionProperties:
ssl: false
envProperties:
envProperties: # Not used yet
enableDatetimeFormatting: true
timeFormat: 'HH:mm:ss'
dateFormat: 'dd-MM-yyyy'
Expand Down
2 changes: 1 addition & 1 deletion api-rest/src/main/resources/application-mongo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ app:
password: homi2022
connectionProperties:
ssl: false
envProperties:
envProperties: # Not used yet
enableDatetimeFormatting: true
timeFormat: 'HH:mm:ss'
dateFormat: 'dd-MM-yyyy'
Expand Down
7 changes: 7 additions & 0 deletions api-rest/src/main/resources/application-pg15.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
app:
databases:
- name: ${DB_NAME:pgdb}
type: POSTGRESQL
url: jdbc:postgresql://localhost:5432/postgres
username: postgres
password:
7 changes: 7 additions & 0 deletions api-rest/src/main/resources/application-pg16.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
app:
databases:
- name: ${DB_NAME:pgdb}
type: POSTGRESQL
url: jdbc:postgresql://localhost:5432/postgres
username: postgres
password:
12 changes: 10 additions & 2 deletions api-rest/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ spring:

db2rest:

dateTime:
enableDataTimeFormatting: ${ENABLE_DATETIME_FORMATTING:false}
timeFormat: ${TIME_FORMAT:HH:mm:ss}
dateFormat: ${DATE_FORMAT:yyyy-MM-dd}
dateTimeFormat: ${DATE_TIME_FORMAT:yyyy-MM-dd HH:mm:ss}

defaultFetchLimit: ${DEFAULT_FETCH_LIMIT:100}

auth:
type: ${AUTH_TYPE:none}

Expand All @@ -45,6 +53,6 @@ logging:
com.homihq.db2rest:
rest: INFO
jdbc.service: INFO
org.springframework.web : INFO
org.springframework.web : DEBUG
org.springframework.beans : INFO
org.springframework.jdbc: trace
org.springframework.jdbc: INFO
31 changes: 31 additions & 0 deletions auth/src/main/java/com/homihq/db2rest/auth/AuthConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.homihq.db2rest.auth;


import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.homihq.db2rest.auth.jwt.JwtAuthProvider;
import com.homihq.db2rest.auth.jwt.JwtProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "db2rest.auth", name="type" , havingValue = "jwt")
public class AuthConfiguration {

private final JwtProperties jwtProperties;

@Bean
public JwtAuthProvider jwtAuthProvider() {
JWTVerifier jwtVerifier =
JWT.require(jwtProperties.getAlgo())
.withIssuer(jwtProperties.getIssuers())
.build();

return new JwtAuthProvider(jwtVerifier);
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.homihq.db2rest.auth.jwt;
package com.homihq.db2rest.auth;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.homihq.db2rest.auth.jwt.service.JwtAuthService;
import com.homihq.db2rest.auth.common.AuthProvider;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
Expand All @@ -11,49 +11,61 @@
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Optional;

@RequiredArgsConstructor
@Slf4j
public class JwtAuthFilter extends OncePerRequestFilter {
public class AuthFilter extends OncePerRequestFilter {

private final JwtAuthService jwtAuthService;
private final List<AuthProvider> authProviders;
private final ObjectMapper objectMapper;
final String JWT_KEY_HEADER = "Authorization";
String AUTH_HEADER = "Authorization";

@Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain filterChain) throws ServletException, IOException {

String jwtHeader = request.getHeader(JWT_KEY_HEADER);
if (StringUtils.isBlank(jwtHeader) || !jwtHeader.startsWith("Bearer ")) {
addMissingJwtTokenError(request, response);

log.info("Handling Auth");


String authHeaderValue = request.getHeader(AUTH_HEADER);

if(StringUtils.isBlank(authHeaderValue)) {
addMissingAuthTokenError(request, response);
return;
}

String token = StringUtils.replace(jwtHeader, "Bearer ", "", 1);
if (!jwtAuthService.isValidToken(token)) {
Optional<AuthProvider> authProvider = authProviders.stream()
.filter(ap -> ap.canHandle(authHeaderValue))
.findFirst();

if(authProvider.isEmpty()) {
addAuthenticationError(request, response);
return;
}
else{
authProvider.get().handle(authHeaderValue);
}

filterChain.doFilter(request, response);
logger.info("Completed Jwt Auth Filter");
}

logger.info("Completed Auth Filter");
}

private void addAuthenticationError(HttpServletRequest request , HttpServletResponse response) throws IOException{
private void addMissingAuthTokenError(HttpServletRequest request , HttpServletResponse response) throws IOException{
var body = new LinkedHashMap<>();
body.put("type", "https://db2rest/unauthorized");
body.put("title", "Unauthorized");
body.put("type", "https://db2rest/jwt-token-not-found");
body.put("title", "Auth token not provided in header");
body.put("status", HttpServletResponse.SC_UNAUTHORIZED);

body.put("detail", "Invalid JWT Token");
body.put("detail", "Auth token not provided in header. Please add header " + AUTH_HEADER + " with valid Auth token.");
body.put("instance", request.getRequestURI());
body.put("errorCategory", "Unauthorized");
body.put("errorCategory", "Invalid-Auth-Token");
body.put("timestamp", Instant.now());

response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
Expand All @@ -62,15 +74,16 @@ private void addAuthenticationError(HttpServletRequest request , HttpServletResp
objectMapper.writeValue(response.getWriter(), body);
}

private void addMissingJwtTokenError(HttpServletRequest request , HttpServletResponse response) throws IOException{

private void addAuthenticationError(HttpServletRequest request , HttpServletResponse response) throws IOException{
var body = new LinkedHashMap<>();
body.put("type", "https://db2rest/jwt-token-not-found");
body.put("title", "JWT token not provided in header");
body.put("type", "https://db2rest/unauthorized");
body.put("title", "Unauthorized");
body.put("status", HttpServletResponse.SC_UNAUTHORIZED);

body.put("detail", "JWT token not provided in header. Please add header " + JWT_KEY_HEADER + " with valid JWT token.");
body.put("detail", "No Auth Handler found");
body.put("instance", request.getRequestURI());
body.put("errorCategory", "Invalid-JWT-Token");
body.put("errorCategory", "Auth-error");
body.put("timestamp", Instant.now());

response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.homihq.db2rest.auth.basic;

import com.homihq.db2rest.auth.common.AuthDataProvider;
import com.homihq.db2rest.auth.common.AuthProvider;
import com.homihq.db2rest.auth.common.Subject;
import com.homihq.db2rest.auth.common.User;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
import java.util.Base64;


@RequiredArgsConstructor
@Slf4j
public class BasicAuthProvider implements AuthProvider {

private final AuthDataProvider authDataProvider;
@Override
public boolean canHandle(String authHeader) {
return StringUtils.isNotBlank(authHeader) && authHeader.startsWith("Basic ");
}

@Override
public Subject handle(String authHeader) {
String base64Credentials = authHeader.substring("Basic ".length());
byte[] decodedCredentials = Base64.getDecoder().decode(base64Credentials);
String credentials = new String(decodedCredentials, StandardCharsets.UTF_8);

String[] parts = credentials.split(":");
String username = parts[0];
String password = parts[1];

User user = authDataProvider.validate(username, password);

return new Subject(username, user.roles(), "");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.homihq.db2rest.auth.common;

import java.util.List;

public record ApiResource(String path, String method, List<String> roles) {
}
17 changes: 0 additions & 17 deletions auth/src/main/java/com/homihq/db2rest/auth/common/AuthContext.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.homihq.db2rest.auth.common;

import com.homihq.db2rest.auth.exception.AuthException;
import org.apache.commons.lang3.StringUtils;

import java.util.List;

public interface AuthDataProvider {

List<ApiResource> getApiResources();
List<User> getUsers();

default User validate(String username, String password) {
return
getUsers().stream()
.filter(u -> StringUtils.equals(u.username(), username)
&& StringUtils.equals(u.username(), password)).findFirst().orElseThrow(() ->
new AuthException("Invalid username or password."));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.homihq.db2rest.auth.common;

public interface AuthProvider {

boolean canHandle(String authHeader);

Subject handle(String authHeader);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.homihq.db2rest.auth.common;

import java.util.List;

public record Subject (String principal, List<String> roles, String requestedUri) { }
6 changes: 6 additions & 0 deletions auth/src/main/java/com/homihq/db2rest/auth/common/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.homihq.db2rest.auth.common;

import java.util.List;

public record User(String username, String password, List<String> roles) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.homihq.db2rest.auth.data;

import com.homihq.db2rest.auth.common.ApiResource;
import com.homihq.db2rest.auth.common.AuthDataProvider;
import com.homihq.db2rest.auth.common.User;

import java.util.List;

public class FileAuthDataProvider implements AuthDataProvider {


@Override
public List<ApiResource> getApiResources() {
return null;
}

@Override
public List<User> getUsers() {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.homihq.db2rest.auth.exception;

import org.springframework.core.NestedRuntimeException;

public class AuthException extends NestedRuntimeException {
public AuthException(String msg) {
super(msg);
}
}
Loading

0 comments on commit 18701e7

Please sign in to comment.