Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement java server #1203

Open
wants to merge 86 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
69d50cb
init server-java/gradle
Jul 27, 2020
82059c2
added .gitignore
Jul 27, 2020
5e6003f
added Dockerfile for java
Jul 27, 2020
049b827
added docker-compose with react and java
Jul 27, 2020
de0d324
init spring boot app with default configs
Jul 27, 2020
2effec0
added core module
Jul 27, 2020
3acdca5
added default graphQL schema
Jul 27, 2020
dea5219
added publisher and subscriber for graphQL subscription
Jul 27, 2020
1e618db
init counter module
Jul 27, 2020
13af767
added counter graphQL schema
Jul 27, 2020
66f41da
added counter model, repository + db initializer
Jul 27, 2020
f7022b0
added counter pub/sub service
Jul 27, 2020
b496f6b
added counter graphQL resolvers
Jul 27, 2020
1ef76c7
added user module + graphQL schema
Jul 27, 2020
1fd5f30
added entities(models) for user module
Jul 27, 2020
0c50adc
added DTOs related to graphQL input types
Jul 27, 2020
51480b8
added repositories
Jul 27, 2020
fe1a05d
added DB initializer when the application starts
Jul 27, 2020
05dc371
added implementation of pub/sub service for user
Jul 27, 2020
66fced7
added graphQL resolvers for user
Jul 27, 2020
e2c9b91
fixed docker-compose
Jul 28, 2020
8f5c907
added tests for counter module
Jul 28, 2020
ed348f2
added tests for user module
Jul 28, 2020
de22da7
format code
Jul 28, 2020
9055d99
added graphQL schema for register/login/actions with password
Aug 4, 2020
dd96044
updated dependency and gradle versions
vitalikmordak Jan 4, 2021
a1c857c
updated docker files
vitalikmordak Jan 4, 2021
0f410aa
added cors configuration
vitalikmordak Jan 4, 2021
21d3e08
refactoring user module
vitalikmordak Jan 4, 2021
b06979a
initiate all users when starting app
vitalikmordak Jan 5, 2021
b697cd8
added models for login
vitalikmordak Jan 5, 2021
41f890e
implemented logout
vitalikmordak Jan 5, 2021
8194b62
implemented jwt + login + refactoring
vitalikmordak Jan 5, 2021
f998d4d
added JwtGenerator interface
vitalikmordak Jan 5, 2021
fe0dd3d
Merge branch 'implement-java-server-jwt-and-auth' into implement-java…
vitalikmordak Jan 6, 2021
af533ec
updated build configuration and Dockerfile
vitalikmordak Jan 6, 2021
53ef7fe
implemented refrehTokens API
vitalikmordak Jan 6, 2021
4257a02
moved refreshTokens schema
vitalikmordak Jan 6, 2021
a0908d3
removed CORS config
vitalikmordak Jan 6, 2021
a23e697
fixed and improve login/logout
vitalikmordak Jan 6, 2021
dd2bf58
refactoring graphQL resolvers + enable async + fixed tests
vitalikmordak Jan 6, 2021
433ff0f
added cors
vitalikmordak Jan 8, 2021
93c4e56
moved refreshTokens API to authentication module + refactoring/improv…
vitalikmordak Jan 8, 2021
eac9b56
added check on a blank string for a refresh token
vitalikmordak Jan 8, 2021
f04de26
implemented graphql error handling
vitalikmordak Jan 8, 2021
bfbf45f
added checks on login and register user
vitalikmordak Jan 8, 2021
6d201df
made skeleton for mailer module
vitalikmordak Jan 8, 2021
5f040b7
removed redundant graphql property
vitalikmordak Jan 8, 2021
4ee6236
identify mailer module in settings.gradle
vitalikmordak Jan 8, 2021
ff8432e
added registration confirm functionality
vitalikmordak Jan 13, 2021
c4e9152
added registration confirm functionality
vitalikmordak Jan 13, 2021
b84c361
updated mail configuration
vitalikmordak Jan 14, 2021
4b00365
fixed mail configuration
vitalikmordak Jan 14, 2021
7656cd1
implemented contact module
vitalikmordak Jan 14, 2021
ca31393
removed redundant dependency
vitalikmordak Jan 14, 2021
47ba270
implemented upload module
vitalikmordak Jan 15, 2021
c8d0380
added post module skeleton; added dtos
vitalikmordak Jan 15, 2021
dff2f7d
implemented post module
vitalikmordak Jan 19, 2021
72f4bdf
refactoring; cleaning up code
vitalikmordak Jan 19, 2021
422ebfd
moved custom pageable implementation to core module
vitalikmordak Jan 20, 2021
d15d40f
initialized skeleton for chat module
vitalikmordak Jan 20, 2021
31ee83c
updated java version to 14
vitalikmordak Jan 25, 2021
d862f97
added pub-sub for chat module
vitalikmordak Jan 25, 2021
80fa7d0
refactored upload module
vitalikmordak Jan 25, 2021
01967fb
use delete method from service for upload module
vitalikmordak Jan 25, 2021
654b554
implemented mutation and query resolvers
vitalikmordak Jan 25, 2021
355ddbd
delete gitkeep
vitalikmordak Jan 25, 2021
f1c397b
upgraded switch expression
vitalikmordak Jan 26, 2021
f5cb544
updated lombok configuration for input DTOs
vitalikmordak Jan 26, 2021
412d066
fixed issue connected with adding new active user
vitalikmordak Jan 26, 2021
680625b
refactoring resolvers method for user and upload modules
vitalikmordak Jan 26, 2021
321feb6
implemented reports module
vitalikmordak Jan 26, 2021
03b5a90
added tests for user module
vitalikmordak Jan 26, 2021
6f463d3
resolved issue with failed tests; added new tests for forgot and rese…
vitalikmordak Jan 27, 2021
81a0baf
fixed confirm registration endpoint
vitalikmordak Jan 28, 2021
4123730
added tests for registration and confirm registration
vitalikmordak Jan 28, 2021
a098c5d
added tests for refresh token
vitalikmordak Feb 4, 2021
9d3ba09
added tests for reset password + refactoring
vitalikmordak Feb 4, 2021
c39caf9
added test for logout user
vitalikmordak Feb 8, 2021
e0f815c
added tests for post module + refactoring DTOs
vitalikmordak Feb 8, 2021
69b76ac
added tests for chat module
vitalikmordak Feb 12, 2021
41b828b
fixed issue with empty user for chat
vitalikmordak Feb 12, 2021
08aa578
added tests for chat and upload modules
vitalikmordak Feb 16, 2021
301debf
implemented i18n module
vitalikmordak Feb 19, 2021
576f8cf
added i18n support for user module
vitalikmordak Feb 19, 2021
3e66358
fixed condition for permissions when deleting user; fixed test and ad…
vitalikmordak Feb 19, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Dockerfile-java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM openjdk:11-jdk as BUILD_IMAGE
COPY ./packages/server-java ./packages/server-java
COPY ./modules ./modules

WORKDIR ./packages/server-java
RUN ./gradlew clean bootJar

FROM openjdk:11-jre-slim
WORKDIR /server-java/
COPY --from=BUILD_IMAGE /packages/server-java/app/build/libs/app-release.jar .
EXPOSE 8080
CMD ["java", "-jar", "app-release.jar"]
48 changes: 48 additions & 0 deletions docker-compose.react-java.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
version: '3'
services:
apollo_java_server:
build:
context: .
dockerfile: Dockerfile-java
environment:
- JAVA_ENV=development
container_name: apollo_java_server
volumes:
- ./:${APP_DIR}
- ./modules:${APP_DIR}/modules
- ./packages/server-java/gradle/wrapper:${APP_DIR}/packages/server-java/gradle/wrapper
- ./packages/server-java/gradlew:${APP_DIR}/packages/server-java/gradlew
ports:
- 8080:8080
stdin_open: true
tty: true
network_mode: host

apollo_client:
build:
context: .
dockerfile: Dockerfile
args:
APP_DIR: ${APP_DIR}
depends_on:
- apollo_java_server
environment:
- NODE_ENV=development
- SERVER_HOST=apollo_java_server:8080
container_name: apollo_client
tty: true
stdin_open: true
volumes:
- ./:${APP_DIR}
- ${APP_DIR}/build
- client_node_modules:${APP_DIR}/node_modules
working_dir: ${APP_DIR}
user: node
command: >
sh -c 'cmp -s yarn.lock node_modules/yarn.lock || yarn install --frozen-lockfile && cp -f yarn.lock node_modules/yarn.lock && yarn watch-client'
ports:
- 3000:3000
network_mode: host

volumes:
client_node_modules:
7 changes: 7 additions & 0 deletions modules/authentication/server-java/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dependencies {
implementation project(':core')
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.2'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2'
api 'org.springframework.boot:spring-boot-starter-security'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.sysgears.authentication.config;

import io.jsonwebtoken.security.Keys;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.security.Key;

@Configuration
public class AuthConfig {
@Bean
public Key jwtSecretKey(JwtConfig config) {
return Keys.hmacShaKeyFor(config.getSecret().getBytes());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.sysgears.authentication.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Getter
@Setter
@Configuration
@ConfigurationProperties("jwt")
public class JwtConfig {
private String secret;
private long accessTokenExpirationInSec;
private long refreshTokenExpirationInSec;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.sysgears.authentication.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

public class RefreshTokenInvalidException extends RuntimeException {

public RefreshTokenInvalidException() {
super("Refresh token is invalid.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.sysgears.authentication.model.jwt;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class JwtUserIdentity {
private final int id;
private final String username;
private final String passwordHash;
private final String role;
private final Boolean isActive;
private final String email;
private final String firstName;
private final String lastName;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.sysgears.authentication.model.jwt;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Tokens {
private String accessToken;
private String refreshToken;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.sysgears.authentication.resolvers.jwt;

import com.sysgears.authentication.exception.RefreshTokenInvalidException;
import com.sysgears.authentication.model.jwt.JwtUserIdentity;
import com.sysgears.authentication.model.jwt.Tokens;
import com.sysgears.authentication.service.jwt.JwtGenerator;
import com.sysgears.authentication.service.jwt.JwtParser;
import graphql.kickstart.tools.GraphQLMutationResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.concurrent.CompletableFuture;

@Component
@RequiredArgsConstructor
public class JwtMutationResolver implements GraphQLMutationResolver {
private final JwtParser jwtParser;
private final JwtGenerator jwtGenerator;
private final JwtUserIdentityService userIdentityService;

public CompletableFuture<Tokens> refreshTokens(String refreshToken) {
return CompletableFuture.supplyAsync(() -> {
if (refreshToken.isBlank()) {
throw new IllegalArgumentException();
}
Integer userId = jwtParser.getIdFromRefreshToken(refreshToken);
JwtUserIdentity userIdentity = userIdentityService.findById(userId).orElseThrow(RefreshTokenInvalidException::new);
return jwtGenerator.generateTokens(userIdentity);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.sysgears.authentication.resolvers.jwt;

import com.sysgears.authentication.model.jwt.JwtUserIdentity;

import java.util.Optional;

public interface JwtUserIdentityService {
Optional<JwtUserIdentity> findById(Integer id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.sysgears.authentication.resolvers.session;

import com.sysgears.authentication.utils.SessionUtils;
import graphql.kickstart.tools.GraphQLMutationResolver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class SessionMutationResolver implements GraphQLMutationResolver {

public String logout() {
if (SessionUtils.SECURITY_CONTEXT.getAuthentication() != null) {
log.info("Logout user '{}'", SessionUtils.SECURITY_CONTEXT.getAuthentication().getName());
SessionUtils.SECURITY_CONTEXT.setAuthentication(null);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sysgears.authentication.service.jwt;

import com.sysgears.authentication.model.jwt.JwtUserIdentity;
import com.sysgears.authentication.model.jwt.Tokens;

public interface JwtGenerator {
Tokens generateTokens(JwtUserIdentity identity);

String generateVerificationToken(JwtUserIdentity identity);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.sysgears.authentication.service.jwt;

public interface JwtParser {
Integer getIdFromAccessToken(String token);

Integer getIdFromRefreshToken(String token);

Integer getIdFromVerificationToken(String token);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.sysgears.authentication.service.jwt;

import com.sysgears.authentication.config.JwtConfig;
import com.sysgears.authentication.model.jwt.JwtUserIdentity;
import com.sysgears.authentication.model.jwt.Tokens;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.security.Key;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class JwtService implements JwtGenerator, JwtParser {
private final Key secretKey;
private final JwtConfig jwtConfig;

public Tokens generateTokens(JwtUserIdentity identity) {
return new Tokens(generateAccessToken(identity), generateRefreshToken(identity));
}

public String generateVerificationToken(JwtUserIdentity identity) {
return Jwts.builder()
.setSubject(String.valueOf(identity.getId()))
.setIssuedAt(Date.from(Instant.now()))
.setExpiration(Date.from(Instant.now().plus(jwtConfig.getAccessTokenExpirationInSec(), ChronoUnit.SECONDS)))
.setHeaderParam("typ", "JWT")
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
}

public Integer getIdFromAccessToken(String token) {
return (Integer) Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody()
.get("identity", Map.class)
.get("id");
}

public Integer getIdFromRefreshToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody()
.get("id", Integer.class);
}

public Integer getIdFromVerificationToken(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody();
return Integer.parseInt(claims.getSubject());
}

private String generateAccessToken(JwtUserIdentity identity) {
Map<String, Object> claims = new HashMap<>();
claims.put("identity", identity);
log.debug("Generating new access JWT for user {}", identity.getId());

return Jwts.builder()
.setClaims(claims)
.setIssuedAt(Date.from(Instant.now()))
.setExpiration(Date.from(Instant.now().plus(jwtConfig.getAccessTokenExpirationInSec(), ChronoUnit.SECONDS)))
.setHeaderParam("typ", "JWT")
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
}

private String generateRefreshToken(JwtUserIdentity identity) {
Map<String, Object> claims = new HashMap<>();
claims.put("id", identity.getId());
log.debug("Generating new refresh JWT for user {}", identity.getId());

return Jwts.builder()
.setClaims(claims)
.setIssuedAt(Date.from(Instant.now()))
.setExpiration(Date.from(Instant.now().plus(jwtConfig.getRefreshTokenExpirationInSec(), ChronoUnit.SECONDS)))
.setHeaderParam("typ", "JWT")
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.sysgears.authentication.utils;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class SessionUtils {
public static final SecurityContext SECURITY_CONTEXT = SecurityContextHolder.createEmptyContext();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
extend type Mutation {
# Refresh user tokens
refreshTokens(refreshToken: String!): Tokens!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
extend type Mutation {
# Logout user
logout: String
}
Loading