Skip to content

Commit

Permalink
Merge pull request #91 from Arquisoft/develop
Browse files Browse the repository at this point in the history
v0.5.0
  • Loading branch information
Toto-hitori authored Mar 6, 2024
2 parents 42e1499 + 14895bd commit 9b87aca
Show file tree
Hide file tree
Showing 96 changed files with 1,897 additions and 16,811 deletions.
43 changes: 24 additions & 19 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,27 @@ jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm --prefix users/authservice ci
- run: npm --prefix users/userservice ci
- run: npm --prefix gatewayservice ci
- run: npm --prefix webapp ci
- run: npm --prefix users/authservice test -- --coverage
- run: npm --prefix users/userservice test -- --coverage
- run: npm --prefix gatewayservice test -- --coverage
- run: npm --prefix webapp test -- --coverage
- name: Analyze with SonarCloud
uses: sonarsource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: actions/checkout@v4
- run: npm --prefix webapp ci
- run: npm --prefix webapp test -- --coverage
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- run: mvn clean verify
working-directory: api
env:
DATABASE_USER: ${{ secrets.DATABASE_USER }}
DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
- name: Analyze with SonarCloud
uses: sonarsource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
53 changes: 30 additions & 23 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,28 @@ jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm --prefix users/authservice ci
- run: npm --prefix users/userservice ci
- run: npm --prefix gatewayservice ci
- run: npm --prefix webapp ci
- run: npm --prefix users/authservice test -- --coverage
- run: npm --prefix users/userservice test -- --coverage
- run: npm --prefix gatewayservice test -- --coverage
- run: npm --prefix webapp test -- --coverage
- name: Analyze with SonarCloud
uses: sonarsource/sonarcloud-github-action@master
env:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: actions/checkout@v4
- run: npm --prefix webapp ci
- run: npm --prefix webapp test -- --coverage
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- run: mvn clean verify
working-directory: api
env:
DATABASE_USER: ${{ secrets.DATABASE_USER }}
DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
- name: Analyze with SonarCloud
uses: sonarsource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
e2e-tests:
Expand All @@ -33,12 +40,9 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm --prefix users/authservice install
- run: npm --prefix users/userservice install
- run: npm --prefix gatewayservice install
- run: npm --prefix webapp install
- run: npm --prefix webapp run build
- run: npm --prefix webapp run test:e2e
#- run: npm --prefix webapp run test:e2e TODO: re-enable
docker-push-api:
runs-on: ubuntu-latest
needs: [ e2e-tests ]
Expand Down Expand Up @@ -74,7 +78,10 @@ jobs:
user: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_KEY }}
command: |
wget https://raw.githubusercontent.com/arquisoft/wiq_en2b/master/docker-compose.yml -O docker-compose.yml
wget https://raw.githubusercontent.com/arquisoft/wiq_en2b/master/.env -O .env
docker compose down
docker compose --profile prod up -d
git clone https://github.com/Arquisoft/wiq_en2b.git
cd wiq_en2b
echo "DATABASE_USER=${{ secrets.DATABASE_USER }}" >> .env
echo "DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }}" >> .env
echo "JWT_SECRET=${{ secrets.JWT_SECRET }}" >> .env
sudo docker compose down
sudo docker compose --profile prod up -d
1 change: 1 addition & 0 deletions api/lombok.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lombok.addLombokGeneratedAnnotation = true
47 changes: 47 additions & 0 deletions api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
<description>Api for quiz ap[l]i[cation]</description>
<properties>
<java.version>17</java.version>
<!-- JaCoCo Properties -->
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
<sonar.language>java</sonar.language>
</properties>
<dependencies>
<dependency>
Expand Down Expand Up @@ -43,6 +48,11 @@
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
Expand Down Expand Up @@ -78,6 +88,16 @@
<artifactId>jjwt-jackson</artifactId>
<version>0.12.1</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</dependency>
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
</dependency>
</dependencies>

<build>
Expand All @@ -94,6 +114,33 @@
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<configuration>
<excludes>
<exclude>**/quizapi/commons/utils/**</exclude>
<exclude>**/quizapi/commons/exceptions/**</exclude>
<exclude>**/quizapi/auth/jwt/**</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-site</id>
<phase>package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down
30 changes: 17 additions & 13 deletions api/src/main/java/lab/en2b/quizapi/auth/AuthController.java
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
package lab.en2b.quizapi.auth;

import jakarta.validation.Valid;
import lab.en2b.quizapi.auth.dtos.LoginDto;
import lab.en2b.quizapi.auth.dtos.RefreshTokenDto;
import lab.en2b.quizapi.auth.dtos.RegisterDto;
import lab.en2b.quizapi.auth.dtos.*;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;

@PostMapping("/register")
public ResponseEntity<?> registerUser(@Valid @RequestBody RegisterDto registerRequest){
return authService.register(registerRequest);
public ResponseEntity<String> registerUser(@Valid @RequestBody RegisterDto registerRequest){
authService.register(registerRequest);
return ResponseEntity.ok("User registered successfully!");
}

@PostMapping("/login")
public ResponseEntity<?> loginUser(@Valid @RequestBody LoginDto loginRequest){
return authService.login(loginRequest);
public ResponseEntity<JwtResponseDto> loginUser(@Valid @RequestBody LoginDto loginRequest){
return ResponseEntity.ok(authService.login(loginRequest));
}

@GetMapping("/logout")
public ResponseEntity<Void> logoutUser(Authentication authentication){
authService.logOut(authentication);
return ResponseEntity.noContent().build();
}

@PostMapping("/refresh-token")
public ResponseEntity<?> refreshToken(@Valid @RequestBody RefreshTokenDto refreshTokenRequest){
return authService.refreshToken(refreshTokenRequest);
public ResponseEntity<RefreshTokenResponseDto> refreshToken(@Valid @RequestBody RefreshTokenDto refreshTokenRequest){
return ResponseEntity.ok(authService.refreshToken(refreshTokenRequest));
}
}
26 changes: 13 additions & 13 deletions api/src/main/java/lab/en2b/quizapi/auth/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,47 @@
import lab.en2b.quizapi.commons.user.User;
import lab.en2b.quizapi.commons.user.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Set;

@Service
@RequiredArgsConstructor
public class AuthService {
private final JwtUtils jwtUtils;
private final AuthenticationManager authenticationManager;
private final UserService userService;
private final JwtUtils jwtUtils;
/**
* Creates a session for a user. Throws an 401 unauthorized exception otherwise
* @param loginRequest the request containing the login info
* @return a response containing a fresh jwt token and a refresh token
*/
@Transactional
public ResponseEntity<JwtResponseDto> login(LoginDto loginRequest){
public JwtResponseDto login(LoginDto loginRequest){
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getEmail(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();

return ResponseEntity.ok(new JwtResponseDto(
return new JwtResponseDto(
jwtUtils.generateJwtTokenUserPassword(authentication),
userService.assignNewRefreshToken(userDetails.getId()),
userDetails.getId(),
userDetails.getUsername(),
userDetails.getEmail(),
userDetails.getStringRoles())
userDetails.getStringRoles()
);
}

/**
* Registers a user. Throws an 400 unauthorized exception otherwise
* @param registerRequest the request containing the register info
* @return a response containing a message
*/
public ResponseEntity<?> register(RegisterDto registerRequest) {
userService.createUser(registerRequest,Set.of("user"));
return ResponseEntity.ok("User registered successfully!");
public void register(RegisterDto registerRequest) {
userService.createUser(registerRequest,"user");
}

/**
Expand All @@ -62,9 +57,14 @@ public ResponseEntity<?> register(RegisterDto registerRequest) {
* @param refreshTokenRequest the request containing the refresh token
* @return a response containing a fresh jwt token and a refresh token
*/
public ResponseEntity<?> refreshToken(RefreshTokenDto refreshTokenRequest) {
public RefreshTokenResponseDto refreshToken(RefreshTokenDto refreshTokenRequest) {
User user = userService.findByRefreshToken(refreshTokenRequest.getRefreshToken()).orElseThrow(() -> new TokenRefreshException(
"Refresh token is not in database!"));
return ResponseEntity.ok(new RefreshTokenResponseDto(jwtUtils.generateTokenFromEmail(user.getEmail()), user.obtainRefreshIfValid()));
return new RefreshTokenResponseDto(jwtUtils.generateTokenFromEmail(user.getEmail()), user.obtainRefreshIfValid());
}

public void logOut(Authentication authentication) {
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
userService.deleteRefreshToken(userDetails.getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Collections;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
Expand All @@ -38,7 +40,7 @@ public CorsFilter corsFilter() {
// Configure CORS settings here
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.setAllowedOriginPatterns(Collections.singletonList("*"));
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
Expand All @@ -56,13 +58,13 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, Authentication
.cors(Customizer.withDefaults())
.sessionManagement(configuration -> configuration.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(HttpMethod.GET,"/auth/logout").authenticated()
.requestMatchers(HttpMethod.POST,"/auth/**").permitAll()
.anyRequest().authenticated())
.csrf(AbstractHttpConfigurer::disable)
.authenticationManager(authenticationManager)
.addFilterBefore(authenticationJwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
.build();
//TODO: add exception handling
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import com.fasterxml.jackson.annotation.JsonIgnore;
import lab.en2b.quizapi.commons.user.User;
import lab.en2b.quizapi.commons.user.role.Role;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
Expand All @@ -16,6 +16,7 @@
import java.util.stream.Collectors;

@Getter
@Setter
@AllArgsConstructor
public class UserDetailsImpl implements UserDetails {
private Long id;
Expand All @@ -26,9 +27,7 @@ public class UserDetailsImpl implements UserDetails {
private Collection<? extends GrantedAuthority> authorities;
public static UserDetailsImpl build(User user) {
List<GrantedAuthority> authorities = new ArrayList<>();
for(Role role : user.getRoles()){
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
authorities.add(new SimpleGrantedAuthority(user.getRole()));
return new UserDetailsImpl(user.getId(),user.getUsername() , user.getEmail(), user.getPassword(), authorities);
}
@Override
Expand Down Expand Up @@ -61,4 +60,9 @@ public boolean equals(Object o) {
UserDetailsImpl user = (UserDetailsImpl) o;
return Objects.equals(id, user.id);
}

@Override
public int hashCode() {
return Objects.hash(id, username, email, password, authorities);
}
}
Loading

0 comments on commit 9b87aca

Please sign in to comment.