Skip to content

Commit

Permalink
Merge branch 'develop' into feat/#108
Browse files Browse the repository at this point in the history
  • Loading branch information
kimday0326 committed Feb 4, 2024
2 parents a3139b3 + 77c6d01 commit d8cfe14
Show file tree
Hide file tree
Showing 17 changed files with 181 additions and 45 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/ci_gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ jobs:
cloud.aws.region.static: ${{ secrets.REGION }}
cloud.aws.credentials.accessKey: ${{ secrets.S3_ACCESS_KEY }}
cloud.aws.credentials.secretKey: ${{ secrets.S3_SECRET_KEY }}
mail.smtp.port: ${{ secrets.EMAIL_PORT }}
AdminMail.id: ${{ secrets.EMAIL_ID }}
AdminMail.password: ${{ secrets.EMAIL_PASSWORD }}



- name: 3) Set prod.yml - Debug
run: |
Expand Down
30 changes: 27 additions & 3 deletions http/test.http
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ Content-Type: application/json
"password": "password1234"
}

### 토큰 재발급
GET http://localhost:8080/api/v1/auth/reissue
Authorization: Bearer {{masterToken}}
RefreshToken: {{refreshToken}}

### 공고 생성
POST http://localhost:8080/api/v1/announcements
Authorization: Bearer {{masterToken}}
Expand Down Expand Up @@ -143,16 +148,36 @@ Authorization: Bearer {{matsterToken}}

### 조직 링크 생성 (TODO: 테스트 필요)
POST http://localhost:8080/api/v1/organization-links
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZW1haWwiOiJzcG9udXNAZ21haWwuY29tIiwiYXV0aCI6IlNUVURFTlQiLCJpYXQiOjE3MDcwMjI2MzIsImV4cCI6MTcwODAyMjYzMn0.olnLsJGmP9hRifXYN-H85V6LBivGhRX8HcPJV1rPSoo
Content-Type: application/json

{
"name": "페이스북 공식 계정",
"url": "https://www.facebook.com/?locale=ko_KR"
}

### 조직 링크 조회
GET http://localhost:8080/api/v1/organization-links/3
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZW1haWwiOiJzcG9udXNAZ21haWwuY29tIiwiYXV0aCI6IlNUVURFTlQiLCJpYXQiOjE3MDcwMjYyNTAsImV4cCI6MTcwODAyNjI1MH0.kCsg2CClbDzBbiX4k2EmxihyToIdr5stZ1ADxoMdSSI


### 조직 링크 수정
PATCH http://localhost:8080/api/v1/organization-links/1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZW1haWwiOiJzcG9udXNAZ21haWwuY29tIiwiYXV0aCI6IlNUVURFTlQiLCJpYXQiOjE3MDcwMjYyNTAsImV4cCI6MTcwODAyNjI1MH0.kCsg2CClbDzBbiX4k2EmxihyToIdr5stZ1ADxoMdSSI
Content-Type: application/json

{
"name": "페이스북 공식 계정 업데이트",
"url": "https://www.facebook.com/?locale=ko_KR 업데이트"
}

### 조직 링크 삭제
DELETE http://localhost:8080/api/v1/organization-links/1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZW1haWwiOiJzcG9udXNAZ21haWwuY29tIiwiYXV0aCI6IlNUVURFTlQiLCJpYXQiOjE3MDcwMjYyNTAsImV4cCI6MTcwODAyNjI1MH0.kCsg2CClbDzBbiX4k2EmxihyToIdr5stZ1ADxoMdSSI

### 태그 생성
POST http://localhost:8080/api/v1/tags
Authorization: Bearer {{masterToken}}
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZW1haWwiOiJzcG9udXNAZ21haWwuY29tIiwiYXV0aCI6IlNUVURFTlQiLCJpYXQiOjE3MDcwMjI2MzIsImV4cCI6MTcwODAyMjYzMn0.olnLsJGmP9hRifXYN-H85V6LBivGhRX8HcPJV1rPSoo
Content-Type: application/json

{
Expand All @@ -174,5 +199,4 @@ Content-Type: application/json

### 태그 삭제 (TODO: 테스트 필요)
DELETE http://localhost:8080/api/v1/tags/1
Authorization: Bearer {{masterToken}}

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZW1haWwiOiJzcG9udXNAZ21haWwuY29tIiwiYXV0aCI6IlNUVURFTlQiLCJpYXQiOjE3MDcwMjI0MjUsImV4cCI6MTcwODAyMjQyNX0.DxcAbDqwO-aZb6CLof2eRTtxBNHri1-mUAfdsCRPTkU
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.sponus.sponusbe.auth.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.sponus.sponusbe.auth.jwt.dto.JwtPair;
import com.sponus.sponusbe.auth.jwt.exception.SecurityCustomException;
import com.sponus.sponusbe.auth.jwt.exception.SecurityErrorCode;
import com.sponus.sponusbe.auth.jwt.util.JwtUtil;
import com.sponus.sponusbe.global.common.ApiResponse;

import io.jsonwebtoken.ExpiredJwtException;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@RequestMapping("api/v1/auth")
@RestController
public class AuthController {

private final JwtUtil jwtUtil;

@GetMapping("/reissue")
public ApiResponse<JwtPair> reissueToken(@RequestHeader("RefreshToken") String refreshToken) {
try {
jwtUtil.validateRefreshToken(refreshToken);
return ApiResponse.onSuccess(
jwtUtil.reissueToken(refreshToken)
);
} catch (ExpiredJwtException eje) {
throw new SecurityCustomException(SecurityErrorCode.TOKEN_EXPIRED, eje);
} catch (IllegalArgumentException iae) {
throw new SecurityCustomException(SecurityErrorCode.INVALID_TOKEN, iae);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
package com.sponus.sponusbe.auth.jwt.filter;

import static com.sponus.sponusbe.auth.jwt.util.HttpResponseUtil.*;
import static org.springframework.http.HttpStatus.*;

import java.io.IOException;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import com.sponus.sponusbe.auth.jwt.dto.JwtPair;
import com.sponus.sponusbe.auth.jwt.exception.SecurityCustomException;
import com.sponus.sponusbe.auth.jwt.exception.SecurityErrorCode;
import com.sponus.sponusbe.auth.jwt.util.JwtUtil;
import com.sponus.sponusbe.auth.jwt.util.RedisUtil;
import com.sponus.sponusbe.auth.user.CustomUserDetails;
Expand Down Expand Up @@ -60,24 +54,6 @@ protected void doFilterInternal(
filterChain.doFilter(request, response);
} catch (ExpiredJwtException e) {
logger.warn("[*] case : accessToken Expired");
// accessToken 만료 시 Body에 있는 refreshToken 확인
String refreshToken = request.getHeader("refreshToken");

logger.info("[*] refreshToken : " + refreshToken);
try {
if (jwtUtil.validateRefreshToken(refreshToken)) {
logger.info("[*] case : accessToken Expired && refreshToken in redis");
// refreshToken 유효 시 재발급
JwtPair reissueTokens = jwtUtil.reissueToken(refreshToken);
setSuccessResponse(response, CREATED, reissueTokens);
}
} catch (ExpiredJwtException eje) {
logger.info("[*] case : accessToken, refreshToken expired");
throw new SecurityCustomException(SecurityErrorCode.TOKEN_EXPIRED, eje);
} catch (IllegalArgumentException iae) {
logger.info("[*] case : Invalid refreshToken");
throw new SecurityCustomException(SecurityErrorCode.INVALID_TOKEN, iae);
}
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/main/java/com/sponus/sponusbe/auth/jwt/util/JwtUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,15 @@ public String createJwtRefreshToken(CustomUserDetails customUserDetails) {
.add("typ", "JWT")
.and()
.subject(customUserDetails.getId().toString())
.claim("email", customUserDetails.getUsername())
.claim(AUTHORITIES_CLAIM_NAME, customUserDetails.getAuthority())
.issuedAt(Date.from(issuedAt))
.expiration(Date.from(expiration))
.signWith(secretKey)
.compact();

redisUtil.save(
refreshToken,
customUserDetails.getEmail(),
refreshToken,
refreshExpMs,
TimeUnit.MILLISECONDS
Expand Down Expand Up @@ -118,7 +120,7 @@ public String resolveAccessToken(HttpServletRequest request) {
return authorization.split(" ")[1];
}

public boolean validateRefreshToken(String refreshToken) {
public void validateRefreshToken(String refreshToken) {
// refreshToken 유효성 검증
String email = getEmail(refreshToken);

Expand All @@ -127,7 +129,6 @@ public boolean validateRefreshToken(String refreshToken) {
log.warn("[*] case : Invalid refreshToken");
throw new SecurityCustomException(SecurityErrorCode.INVALID_TOKEN);
}
return true;
}

public Long getId(String token) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
@Getter
@AllArgsConstructor
public enum AnnouncementErrorCode implements BaseErrorCode {
ANNOUNCEMENT_ERROR(HttpStatus.BAD_REQUEST, "ANC4000", "공지사항 관련 에러"),
ANNOUNCEMENT_ERROR(HttpStatus.BAD_REQUEST, "ANC4000", "공고 관련 에러"),
ANNOUNCEMENT_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "ANC4001", "이미 삭제된 공지사항입니다."),
INVALID_ORGANIZATION(HttpStatus.BAD_REQUEST, "ANC4002", "해당 단체의 공고가 아닙니다."),
INVALID_ANNOUNCEMENT_STATUS(HttpStatus.BAD_REQUEST, "ANC4003", "해당 상태의 공고는 수정할 수 없습니다."),
ANNOUNCEMENT_NOT_IN_PROGRESS(HttpStatus.BAD_REQUEST, "ANC4004", "진행 중인 공고가 아닙니다."),
ANNOUNCEMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "ANC4040", "해당 공지사항이 존재하지 않습니다.");
ANNOUNCEMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "ANC4040", "해당 공고가 존재하지 않습니다.");

private final HttpStatus httpStatus;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.sponus.sponusbe.domain.organizationLink.controller;

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 com.sponus.sponusbe.domain.organizationLink.dto.request.OrganizationLinkUpdateRequest;
import com.sponus.sponusbe.domain.organizationLink.dto.response.OrganizationLinkGetResponse;
import org.springframework.web.bind.annotation.*;

import com.sponus.sponusbe.auth.annotation.AuthOrganization;
import com.sponus.sponusbe.domain.organization.entity.Organization;
Expand All @@ -29,4 +28,22 @@ public ApiResponse<OrganizationLinkCreateResponse> createOrganizationLink(

return ApiResponse.onSuccess(organizationLinkService.createOrganizationLink(organization.getId(), request));
}

@GetMapping("/{organizationLinkId}")
public ApiResponse<OrganizationLinkGetResponse> getOrganizationLink(@PathVariable("organizationLinkId") Long organizationLinkId) {
return ApiResponse.onSuccess(organizationLinkQueryService.getOrganizationLink(organizationLinkId));
}

@PatchMapping("/{organizationLinkId}")
public ApiResponse<Void> updateOrganizationLink(@PathVariable("organizationLinkId") Long organizationLinkId,
@RequestBody OrganizationLinkUpdateRequest request) {
organizationLinkService.updateOrganizationLink(organizationLinkId, request);
return ApiResponse.onSuccess(null);
}

@DeleteMapping("/{organizationLinkId}")
public ApiResponse<Void> deleteOrganizationLink(@PathVariable("organizationLinkId") Long organizationLinkId) {
organizationLinkService.deleteOrganizationLink(organizationLinkId);
return ApiResponse.onSuccess(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.sponus.sponusbe.domain.organizationLink.dto.request;

public record OrganizationLinkUpdateRequest(
String name,
String url
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.sponus.sponusbe.domain.organization.entity.Organization;

import com.sponus.sponusbe.domain.organizationLink.dto.request.OrganizationLinkUpdateRequest;
import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity;
Expand Down Expand Up @@ -41,4 +42,9 @@ public class OrganizationLink {
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "organization_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Organization organization;

public void update(OrganizationLinkUpdateRequest request) {
this.name = request.name() == null ? this.name : request.name();
this.url = request.name() == null ? this.url : request.url();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
public enum OrganizationLinkErrorCode implements BaseErrorCode {
ORGANIZATION_ERROR(HttpStatus.BAD_REQUEST, "ORGLK4000", "단체 관련 에러"),
INVALID_FORMAT(HttpStatus.BAD_REQUEST, "ORGLK4001", "잘못된 형식입니다."),
ORGGANIZATION_LINK_NOT_FOUND(HttpStatus.NOT_FOUND, "ORGLK4040", "존재하지 않는 조직 링크입니다.");
ORGANIZATION_LINK_NOT_FOUND(HttpStatus.NOT_FOUND, "ORGLK4040", "존재하지 않는 조직 링크입니다.");

private final HttpStatus httpStatus;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
package com.sponus.sponusbe.domain.organizationLink.service;

import com.sponus.sponusbe.domain.organization.exception.OrganizationException;
import com.sponus.sponusbe.domain.organizationLink.dto.response.OrganizationLinkGetResponse;
import com.sponus.sponusbe.domain.organizationLink.entity.OrganizationLink;
import com.sponus.sponusbe.domain.organizationLink.repository.OrganizationLinkRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;

import static com.sponus.sponusbe.domain.organizationLink.exception.OrganizationLinkErrorCode.ORGANIZATION_LINK_NOT_FOUND;

@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class OrganizationLinkQueryService {
private final OrganizationLinkRepository organizationLinkRepository;
public OrganizationLinkGetResponse getOrganizationLink(Long organizationLinkId) {
OrganizationLink organizationLink = organizationLinkRepository.findById(organizationLinkId)
.orElseThrow(() -> new OrganizationException(ORGANIZATION_LINK_NOT_FOUND));

return OrganizationLinkGetResponse.from(organizationLink);

}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.sponus.sponusbe.domain.organizationLink.service;

import static com.sponus.sponusbe.domain.organization.exception.OrganizationErrorCode.*;
import static com.sponus.sponusbe.domain.organizationLink.exception.OrganizationLinkErrorCode.ORGANIZATION_LINK_NOT_FOUND;

import com.sponus.sponusbe.domain.organizationLink.dto.request.OrganizationLinkUpdateRequest;
import com.sponus.sponusbe.domain.organizationLink.exception.OrganizationLinkException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -39,4 +42,16 @@ public OrganizationLinkCreateResponse createOrganizationLink(Long organizationId

return new OrganizationLinkCreateResponse(organizationLink.getId());
}

@Transactional
public void updateOrganizationLink(Long organizationLinkId, OrganizationLinkUpdateRequest request) {
OrganizationLink organizationLink = organizationLinkRepository.findById(organizationLinkId)
.orElseThrow(() -> new OrganizationLinkException(ORGANIZATION_LINK_NOT_FOUND));

organizationLink.update(request);
}

public void deleteOrganizationLink(Long organizationLinkId) {
organizationLinkRepository.deleteById(organizationLinkId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
Expand Down Expand Up @@ -75,4 +76,13 @@ public ApiResponse<Void> updatePropose(
proposeService.updatePropose(authOrganization, proposeId, request);
return ApiResponse.onSuccess(null);
}

@DeleteMapping("/{proposeId}")
public ApiResponse<Void> deletePropose(
@AuthOrganization Organization authOrganization,
@PathVariable Long proposeId
) {
proposeService.deletePropose(authOrganization, proposeId);
return ApiResponse.onSuccess(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ public ProposeCreateResponse createPropose(
}

public void updatePropose(Organization authOrganization, Long proposeId, ProposeUpdateRequest request) {
final Propose propose = proposeRepository.findById(proposeId)
.orElseThrow(() -> new ProposeException(ProposeErrorCode.PROPOSE_NOT_FOUND));

if (!isOrganizationsPropose(authOrganization.getId(), propose))
throw new ProposeException(ProposeErrorCode.INVALID_ORGANIZATION);

final Propose propose = getAccessablePropose(authOrganization, proposeId);
propose.update(request.title(), request.content(), request.status());
}

public void deletePropose(Organization authOrganization, Long proposeId) {
final Propose propose = getAccessablePropose(authOrganization, proposeId);
proposeRepository.delete(propose);
}

private boolean isOrganizationsPropose(Long organizationId, Propose propose) {
return propose.getProposingOrganization().getId().equals(organizationId);
}
Expand All @@ -84,4 +84,13 @@ private Announcement getAvailableAnnouncement(Long announcementId) {
return announcement;
}

private Propose getAccessablePropose(Organization organization, Long proposeId) {
final Propose propose = proposeRepository.findById(proposeId)
.orElseThrow(() -> new ProposeException(ProposeErrorCode.PROPOSE_NOT_FOUND));

if (!isOrganizationsPropose(organization.getId(), propose))
throw new ProposeException(ProposeErrorCode.INVALID_ORGANIZATION);

return propose;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class EmailConfig {

@Value("${mail.smtp.port}")
private int port;
@Value("${mail.smtp.socketFactory.port}")
@Value("${mail.smtp.port}")
private int socketPort;
@Value("${mail.smtp.auth}")
private boolean auth;
Expand Down
Loading

0 comments on commit d8cfe14

Please sign in to comment.