diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4b1dc97cb..32efae4fa 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: - service: [main, gateway] + service: [main] steps: - name: Checkout code @@ -48,8 +48,6 @@ jobs: run: | if [ "${{ matrix.service }}" = "main" ]; then ./gradlew :dms-main:test jacocoMainServiceReport --info - elif [ "${{ matrix.service }}" = "gateway" ]; then - ./gradlew :dms-gateway:test jacocoGatewayServiceReport --info fi - name: Upload JaCoCo report artifact @@ -90,16 +88,12 @@ jobs: - name: Move reports to correct location run: | mkdir -p build/reports/jacoco/main-service - mkdir -p build/reports/jacoco/gateway-service cp -r build/reports/jacoco/jacoco-report-main/* build/reports/jacoco/main-service/ 2>/dev/null || true - cp -r build/reports/jacoco/jacoco-report-gateway/* build/reports/jacoco/gateway-service/ 2>/dev/null || true - name: Verify JaCoCo reports exist run: | echo "=== Main service report ===" ls -la build/reports/jacoco/main-service/ || echo "No main service report found" - echo "=== Gateway service report ===" - ls -la build/reports/jacoco/gateway-service/ || echo "No gateway service report found" - name: Delete previous JaCoCo comments run: | @@ -122,13 +116,3 @@ jobs: min-coverage-overall: 24 min-coverage-changed-files: 50 title: '📊 Main Service Coverage Report' - - - name: Comment PR with gateway service coverage - uses: madrapps/jacoco-report@v1.6.1 - with: - paths: | - build/reports/jacoco/gateway-service/jacocoTestReport.xml - token: ${{ secrets.GITHUB_TOKEN }} - min-coverage-overall: 0 - min-coverage-changed-files: 50 - title: '📊 Gateway Service Coverage Report' diff --git a/build.gradle.kts b/build.gradle.kts index 5d76e669a..0ce453baf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,9 +45,7 @@ subprojects { implementation(Dependencies.JACKSON) // java servlet - if (!project.path.contains("dms-gateway")) { - implementation(Dependencies.JAVA_SERVLET) - } + implementation(Dependencies.JAVA_SERVLET) // test testImplementation(Dependencies.SPRING_TEST) @@ -257,80 +255,12 @@ tasks.register("jacocoMainServiceReport") { } } -// dms-gateway 서비스 통합 리포트 -tasks.register("jacocoGatewayServiceReport") { - group = "verification" - description = "Generate Jacoco coverage report for dms-gateway service" - - val gatewayProjects = subprojects.filter { - it.path.startsWith(":dms-gateway") && it.pluginManager.hasPlugin("jacoco") - } - - dependsOn(gatewayProjects.map { it.tasks.named("test") }) - dependsOn(gatewayProjects.map { it.tasks.named("jacocoTestReport") }) - - executionData.setFrom( - gatewayProjects.map { project -> - fileTree(project.layout.buildDirectory.get().asFile).include("jacoco/*.exec") - } - ) - - val sourceSets = gatewayProjects.map { - it.the().getByName("main") - } - - sourceDirectories.setFrom(sourceSets.map { it.allSource.srcDirs }.flatten()) - - classDirectories.setFrom( - files(sourceSets.map { sourceSet -> - sourceSet.output.classesDirs.files.map { dir -> - fileTree(dir) { - exclude( - "**/global/config/**", - "**/thirdparty/**/config/**", - "**/scheduler/config/**", - "**/persistence/**/entity/**", - "**/*Application.class", - "**/*Application\$*.class", - "**/*Properties.class", - "**/*Properties\$*.class", - "**/stub/**" - ) - } - } - }.flatten()) - ) - - reports { - xml.required.set(true) - html.required.set(true) - csv.required.set(false) - - xml.outputLocation.set(layout.buildDirectory.file("reports/jacoco/gateway-service/jacocoTestReport.xml")) - html.outputLocation.set(layout.buildDirectory.dir("reports/jacoco/gateway-service/html")) - } - - doLast { - val reportDir = layout.buildDirectory.dir("reports/jacoco/gateway-service/html").get().asFile - val indexFile = File(reportDir, "index.html") - if (indexFile.exists()) { - val content = indexFile.readText() - val updatedContent = content.replace( - "

DMS-Backend

", - "

DMS-Gateway Service

" - ) - indexFile.writeText(updatedContent) - } - } -} - tasks.register("jacocoAllServiceReports") { group = "verification" description = "Generate Jacoco coverage reports for all services" dependsOn( - "jacocoMainServiceReport", - "jacocoGatewayServiceReport" + "jacocoMainServiceReport" ) doLast { @@ -338,7 +268,6 @@ tasks.register("jacocoAllServiceReports") { println("========================================") println("Jacoco Reports Generated:") println(" - Main Service: build/reports/jacoco/main-service/html/index.html") - println(" - Gateway Service: build/reports/jacoco/gateway-service/html/index.html") println("========================================") } } diff --git a/codecov.yml b/codecov.yml index 5417a5036..a68f09688 100644 --- a/codecov.yml +++ b/codecov.yml @@ -6,7 +6,7 @@ comment: layout: "header,diff,flags,components,files,footer" behavior: default require_changes: false - after_n_builds: 4 # overall + main + notification + gateway + after_n_builds: 1 # main (CI.yml에서 codecov-action을 main-service 1회만 호출) # 서비스별 플래그 설정 flag_management: @@ -18,23 +18,17 @@ flags: paths: - dms-main/ - dms-notification/ - - dms-gateway/ carryforward: false - + main-service: paths: - dms-main/ carryforward: true - + notification-service: paths: - dms-notification/ carryforward: true - - gateway-service: - paths: - - dms-gateway/ - carryforward: true # 서비스별 컴포넌트 설정 component_management: @@ -53,11 +47,6 @@ component_management: name: Notification Service paths: - dms-notification/** - - - component_id: gateway-service - name: Gateway Service - paths: - - dms-gateway/** coverage: precision: 2 diff --git a/dms-gateway/Dockerfile b/dms-gateway/Dockerfile deleted file mode 100644 index 1ce5bab9a..000000000 --- a/dms-gateway/Dockerfile +++ /dev/null @@ -1,85 +0,0 @@ -FROM eclipse-temurin:17-jdk-alpine AS build - -WORKDIR /app - -RUN apk add --no-cache bash findutils gcompat - -ENV GRADLE_OPTS="\ - -Dorg.gradle.daemon=false \ - -Dorg.gradle.parallel=true \ - -Dorg.gradle.configureondemand=true \ - -Dorg.gradle.caching=true \ - -Dorg.gradle.configuration-cache=true \ - -Dorg.gradle.unsafe.configuration-cache=true \ - -Dorg.gradle.unsafe.configuration-cache-problems=warn \ - -Dorg.gradle.workers.max=4 \ - -Dorg.gradle.logging.level=lifecycle \ - -XX:+UseContainerSupport \ - -XX:MaxRAMPercentage=75.0 \ - -XX:+UseG1GC \ - -XX:G1HeapRegionSize=16m \ - -XX:+UseStringDeduplication \ - -XX:-UsePerfData \ - -XX:+DisableExplicitGC \ - -XX:MaxMetaspaceSize=512m \ - -Dkotlin.compiler.execution.strategy=in-process \ - -Dkotlin.incremental=false \ - -Dkotlin.daemon.jvm.options=-Xmx1g \ - -Dkotlin.parallel.tasks.in.project=true \ - -Dfile.encoding=UTF-8 \ - -Duser.country=US \ - -Duser.language=en \ - -Duser.timezone=UTC \ - -Djava.awt.headless=true \ - -Djava.security.egd=file:/dev/./urandom \ - -Dorg.gradle.internal.http.connectionTimeout=60000 \ - -Dorg.gradle.internal.http.socketTimeout=60000 \ - -Dorg.gradle.internal.repository.max.tentatives=1 \ - -Dorg.gradle.internal.repository.initial.backoff=500 \ - -Dorg.gradle.internal.network.retry.max.times=2 \ -" - -ENV GRADLE_USER_HOME="/cache/.gradle" -RUN mkdir -p /cache/.gradle - -COPY ./build.gradle* ./settings.gradle* ./gradle.properties* ./gradlew* ./ -COPY ./gradle ./gradle -COPY ./buildSrc ./buildSrc - -RUN sed -i 's/\r$//' gradlew && chmod +x gradlew -RUN ./gradlew dependencies || true - -COPY ./dms-gateway ./dms-gateway -RUN ./gradlew :dms-gateway:gateway-infrastructure:clean :dms-gateway:gateway-infrastructure:bootJar -x test -RUN rm -rf .gradle /app/.gradle /root/.kotlin /tmp/* /var/tmp/* /tmp/kotlin-daemon*.log* 2>/dev/null || true - - -FROM eclipse-temurin:17-jre-alpine - -RUN apk add --no-cache tzdata curl && \ - addgroup -g 1001 appgroup && \ - adduser -u 1001 -G appgroup -s /bin/sh -D appuser - -WORKDIR /app - -ENV TZ=Asia/Seoul -ENV JAVA_OPTS="\ - -XX:+UseContainerSupport \ - -XX:MaxRAMPercentage=75.0 \ - -XX:+UseG1GC \ - -XX:G1HeapRegionSize=16m \ - -XX:+UseStringDeduplication \ - -XX:-UsePerfData \ -" - -COPY --from=build --chown=appuser:appgroup /app/dms-gateway/gateway-infrastructure/build/libs/*.jar /tmp/libs/ -RUN find /tmp/libs -name "*.jar" ! -name "*-plain.jar" -exec cp {} /app/app.jar \; && \ - rm -rf /tmp/libs && \ - chown appuser:appgroup /app/app.jar - -USER appuser -EXPOSE 8080 - -# 전용 헬스 엔드포인트가 없어 HTTP 응답 수신 여부로 기동 판정 (curl exit 22 = 4xx/5xx 응답도 서버 기동으로 간주) -HEALTHCHECK --interval=10s --timeout=5s --start-period=30s --retries=3 CMD curl -sf -o /dev/null http://localhost:8080/ || [ $? -eq 22 ] -ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"] diff --git a/dms-gateway/build.gradle.kts b/dms-gateway/build.gradle.kts deleted file mode 100644 index 7b46c407f..000000000 --- a/dms-gateway/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -tasks.getByName("jar") { - enabled = false -} \ No newline at end of file diff --git a/dms-gateway/gateway-core/build.gradle.kts b/dms-gateway/gateway-core/build.gradle.kts deleted file mode 100644 index a1241e6c6..000000000 --- a/dms-gateway/gateway-core/build.gradle.kts +++ /dev/null @@ -1,9 +0,0 @@ -dependencies { - // Spring Cloud Gateway - implementation(Dependencies.SPRING_CLOUD_GATEWAY) -} - -tasks.getByName("jar") { - enabled = true - archiveClassifier.set("") -} \ No newline at end of file diff --git a/dms-gateway/gateway-core/config/detekt/detekt.yml b/dms-gateway/gateway-core/config/detekt/detekt.yml deleted file mode 100644 index db8926833..000000000 --- a/dms-gateway/gateway-core/config/detekt/detekt.yml +++ /dev/null @@ -1,810 +0,0 @@ -build: - maxIssues: -1 - excludeCorrectable: false - weights: - # complexity: 2 - # LongParameterList: 1 - # style: 1 - # comments: 1 - -config: - validation: true - warningsAsErrors: false - # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' - excludes: '' - -processors: - active: true - exclude: - - 'DetektProgressListener' - # - 'KtFileCountProcessor' - # - 'PackageCountProcessor' - # - 'ClassCountProcessor' - # - 'FunctionCountProcessor' - # - 'PropertyCountProcessor' - # - 'ProjectComplexityProcessor' - # - 'ProjectCognitiveComplexityProcessor' - # - 'ProjectLLOCProcessor' - # - 'ProjectCLOCProcessor' - # - 'ProjectLOCProcessor' - # - 'ProjectSLOCProcessor' - # - 'LicenseHeaderLoaderExtension' - -console-reports: - active: true - exclude: - - 'ProjectStatisticsReport' - - 'ComplexityReport' - - 'NotificationReport' - # - 'FindingsReport' - - 'FileBasedFindingsReport' - - 'LiteFindingsReport' - -output-reports: - active: true - exclude: - # - 'TxtOutputReport' - # - 'XmlOutputReport' - # - 'HtmlOutputReport' - -comments: - active: true - AbsentOrWrongFileLicense: - active: false - licenseTemplateFile: 'license.template' - licenseTemplateIsRegex: false - CommentOverPrivateFunction: - active: false - CommentOverPrivateProperty: - active: false - DeprecatedBlockTag: - active: false - EndOfSentenceFormat: - active: false - endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' - OutdatedDocumentation: - active: false - matchTypeParameters: true - matchDeclarationsOrder: true - UndocumentedPublicClass: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - searchInNestedClass: true - searchInInnerClass: true - searchInInnerObject: true - searchInInnerInterface: true - UndocumentedPublicFunction: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - UndocumentedPublicProperty: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - -complexity: - active: true - ComplexCondition: - active: true - threshold: 4 - ComplexInterface: - active: false - threshold: 10 - includeStaticDeclarations: false - includePrivateDeclarations: false - ComplexMethod: - active: true - threshold: 15 - ignoreSingleWhenExpression: false - ignoreSimpleWhenEntries: false - ignoreNestingFunctions: false - nestingFunctions: - - 'also' - - 'apply' - - 'forEach' - - 'isNotNull' - - 'ifNull' - - 'let' - - 'run' - - 'use' - - 'with' - LabeledExpression: - active: false - ignoredLabels: [] - LargeClass: - active: true - threshold: 600 - LongMethod: - active: true - threshold: 60 - LongParameterList: - active: true - functionThreshold: 6 - constructorThreshold: 7 - ignoreDefaultParameters: false - ignoreDataClasses: true - ignoreAnnotatedParameter: [] - excludes: ['**/vo/**', '**/usecase/**'] - MethodOverloading: - active: false - threshold: 6 - NamedArguments: - active: false - threshold: 3 - NestedBlockDepth: - active: true - threshold: 4 - ReplaceSafeCallChainWithRun: - active: false - StringLiteralDuplication: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - threshold: 3 - ignoreAnnotation: true - excludeStringsWithLessThan5Characters: true - ignoreStringsRegex: '$^' - TooManyFunctions: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - thresholdInFiles: 11 - thresholdInClasses: 11 - thresholdInInterfaces: 11 - thresholdInObjects: 11 - thresholdInEnums: 11 - ignoreDeprecated: false - ignorePrivate: false - ignoreOverridden: false - -coroutines: - active: true - GlobalCoroutineUsage: - active: false - InjectDispatcher: - active: false - dispatcherNames: - - 'IO' - - 'Default' - - 'Unconfined' - RedundantSuspendModifier: - active: false - SleepInsteadOfDelay: - active: false - SuspendFunWithFlowReturnType: - active: false - -empty-blocks: - active: true - EmptyCatchBlock: - active: true - allowedExceptionNameRegex: '_|(ignore|expected).*' - EmptyClassBlock: - active: true - EmptyDefaultConstructor: - active: true - EmptyDoWhileBlock: - active: true - EmptyElseBlock: - active: true - EmptyFinallyBlock: - active: true - EmptyForBlock: - active: true - EmptyFunctionBlock: - active: true - ignoreOverridden: false - EmptyIfBlock: - active: true - EmptyInitBlock: - active: true - EmptyKtFile: - active: true - EmptySecondaryConstructor: - active: true - EmptyTryBlock: - active: true - EmptyWhenBlock: - active: true - EmptyWhileBlock: - active: true - -exceptions: - active: true - ExceptionRaisedInUnexpectedLocation: - active: true - methodNames: - - 'equals' - - 'finalize' - - 'hashCode' - - 'toString' - InstanceOfCheckForException: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - NotImplementedDeclaration: - active: false - ObjectExtendsThrowable: - active: false - PrintStackTrace: - active: true - RethrowCaughtException: - active: true - ReturnFromFinally: - active: true - ignoreLabeled: false - SwallowedException: - active: true - ignoredExceptionTypes: - - 'InterruptedException' - - 'MalformedURLException' - - 'NumberFormatException' - - 'ParseException' - allowedExceptionNameRegex: '_|(ignore|expected).*' - ThrowingExceptionFromFinally: - active: true - ThrowingExceptionInMain: - active: false - ThrowingExceptionsWithoutMessageOrCause: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - exceptions: - - 'ArrayIndexOutOfBoundsException' - - 'Exception' - - 'IllegalArgumentException' - - 'IllegalMonitorStateException' - - 'IllegalStateException' - - 'IndexOutOfBoundsException' - - 'NullPointerException' - - 'RuntimeException' - - 'Throwable' - ThrowingNewInstanceOfSameException: - active: true - TooGenericExceptionCaught: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - exceptionNames: - - 'ArrayIndexOutOfBoundsException' - - 'Error' - - 'Exception' - - 'IllegalMonitorStateException' - - 'IndexOutOfBoundsException' - - 'NullPointerException' - - 'RuntimeException' - - 'Throwable' - allowedExceptionNameRegex: '_|(ignore|expected).*' - TooGenericExceptionThrown: - active: true - exceptionNames: - - 'Error' - - 'Exception' - - 'RuntimeException' - - 'Throwable' - -formatting: - active: true - android: false - autoCorrect: true - excludes: ['**/SecurityConfig.kt'] - AnnotationOnSeparateLine: - active: false - autoCorrect: true - AnnotationSpacing: - active: false - autoCorrect: true - ArgumentListWrapping: - active: false - autoCorrect: true - indentSize: 4 - maxLineLength: 120 - ChainWrapping: - active: true - autoCorrect: true - CommentSpacing: - active: true - autoCorrect: true - EnumEntryNameCase: - active: false - autoCorrect: true - Filename: - active: true - FinalNewline: - active: true - autoCorrect: true - insertFinalNewLine: true - ImportOrdering: - active: true - autoCorrect: true - layout: '*,java.**,javax.**,kotlin.**,^' - Indentation: - active: true - autoCorrect: true - indentSize: 4 - continuationIndentSize: 4 - MaximumLineLength: - active: true - maxLineLength: 120 - ignoreBackTickedIdentifier: false - ModifierOrdering: - active: true - autoCorrect: true - MultiLineIfElse: - active: false - autoCorrect: true - NoBlankLineBeforeRbrace: - active: true - autoCorrect: true - NoConsecutiveBlankLines: - active: true - autoCorrect: true - NoEmptyClassBody: - active: true - autoCorrect: true - NoEmptyFirstLineInMethodBlock: - active: false - autoCorrect: true - NoLineBreakAfterElse: - active: true - autoCorrect: true - NoLineBreakBeforeAssignment: - active: true - autoCorrect: true - NoMultipleSpaces: - active: true - autoCorrect: true - NoSemicolons: - active: false - autoCorrect: true - NoTrailingSpaces: - active: true - autoCorrect: true - NoUnitReturn: - active: true - autoCorrect: true - NoUnusedImports: - active: true - autoCorrect: true - NoWildcardImports: - active: true - PackageName: - active: false - autoCorrect: true - ParameterListWrapping: - active: true - autoCorrect: true - indentSize: 4 - maxLineLength: 120 - SpacingAroundAngleBrackets: - active: false - autoCorrect: true - SpacingAroundColon: - active: true - autoCorrect: true - SpacingAroundComma: - active: true - autoCorrect: true - SpacingAroundCurly: - active: true - autoCorrect: true - SpacingAroundDot: - active: true - autoCorrect: true - SpacingAroundDoubleColon: - active: false - autoCorrect: true - SpacingAroundKeyword: - active: true - autoCorrect: true - SpacingAroundOperators: - active: true - autoCorrect: true - SpacingAroundParens: - active: true - autoCorrect: true - SpacingAroundRangeOperator: - active: true - autoCorrect: true - SpacingAroundUnaryOperator: - active: false - autoCorrect: true - SpacingBetweenDeclarationsWithAnnotations: - active: false - autoCorrect: true - SpacingBetweenDeclarationsWithComments: - active: false - autoCorrect: true - StringTemplate: - active: true - autoCorrect: true - -naming: - active: true - BooleanPropertyNaming: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - allowedPattern: '^(is|has|are)' - ClassNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - classPattern: '[A-Z][a-zA-Z0-9]*' - ConstructorParameterNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - parameterPattern: '[a-z][A-Za-z0-9]*' - privateParameterPattern: '[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' - ignoreOverridden: true - EnumNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' - ForbiddenClassName: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - forbiddenName: [] - FunctionMaxLength: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - maximumFunctionNameLength: 30 - FunctionMinLength: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - minimumFunctionNameLength: 3 - FunctionNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)' - excludeClassPattern: '$^' - ignoreOverridden: true - FunctionParameterNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - parameterPattern: '[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' - ignoreOverridden: true - InvalidPackageDeclaration: - active: false - rootPackage: '' - LambdaParameterNaming: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - parameterPattern: '[a-z][A-Za-z0-9]*|_' - MatchingDeclarationName: - active: true - mustBeFirst: true - MemberNameEqualsClassName: - active: true - ignoreOverridden: true - NoNameShadowing: - active: false - NonBooleanPropertyPrefixedWithIs: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - ObjectPropertyNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - constantPattern: '[A-Za-z][_A-Za-z0-9]*' - propertyPattern: '[A-Za-z][_A-Za-z0-9]*' - privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' - PackageNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' - TopLevelPropertyNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - constantPattern: '[A-Z][_A-Z0-9]*' - propertyPattern: '[A-Za-z][_A-Za-z0-9]*' - privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' - VariableMaxLength: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - maximumVariableNameLength: 64 - VariableMinLength: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - minimumVariableNameLength: 1 - VariableNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - variablePattern: '[a-z][A-Za-z0-9]*' - privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' - ignoreOverridden: true - -performance: - active: true - ArrayPrimitive: - active: true - ForEachOnRange: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - SpreadOperator: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - UnnecessaryTemporaryInstantiation: - active: true - -potential-bugs: - active: true - AvoidReferentialEquality: - active: false - forbiddenTypePatterns: - - 'kotlin.String' - CastToNullableType: - active: false - Deprecation: - active: false - DontDowncastCollectionTypes: - active: false - DoubleMutabilityForCollection: - active: false - DuplicateCaseInWhenExpression: - active: true - EqualsAlwaysReturnsTrueOrFalse: - active: true - EqualsWithHashCodeExist: - active: true - ExitOutsideMain: - active: false - ExplicitGarbageCollectionCall: - active: true - HasPlatformType: - active: false - IgnoredReturnValue: - active: false - restrictToAnnotatedMethods: true - returnValueAnnotations: - - '*.CheckResult' - - '*.CheckReturnValue' - ignoreReturnValueAnnotations: - - '*.CanIgnoreReturnValue' - ImplicitDefaultLocale: - active: true - ImplicitUnitReturnType: - active: false - allowExplicitReturnType: true - InvalidRange: - active: true - IteratorHasNextCallsNextMethod: - active: true - IteratorNotThrowingNoSuchElementException: - active: true - LateinitUsage: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - ignoreOnClassesPattern: '' - MapGetWithNotNullAssertionOperator: - active: false - MissingPackageDeclaration: - active: false - excludes: ['**/*.kts'] - MissingWhenCase: - active: true - allowElseExpression: true - NullableToStringCall: - active: false - RedundantElseInWhen: - active: true - UnconditionalJumpStatementInLoop: - active: false - UnnecessaryNotNullOperator: - active: true - UnnecessarySafeCall: - active: true - UnreachableCatchBlock: - active: false - UnreachableCode: - active: true - UnsafeCallOnNullableType: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - UnsafeCast: - active: true - UnusedUnaryOperator: - active: false - UselessPostfixExpression: - active: false - WrongEqualsTypeParameter: - active: true - -style: - active: true - ClassOrdering: - active: false - CollapsibleIfStatements: - active: false - DataClassContainsFunctions: - active: false - conversionFunctionPrefix: 'to' - DataClassShouldBeImmutable: - active: false - DestructuringDeclarationWithTooManyEntries: - active: false - maxDestructuringEntries: 3 - EqualsNullCall: - active: true - EqualsOnSignatureLine: - active: false - ExplicitCollectionElementAccessMethod: - active: false - ExplicitItLambdaParameter: - active: false - ExpressionBodySyntax: - active: false - includeLineWrapping: false - ForbiddenComment: - active: true - values: - - 'FIXME:' - - 'STOPSHIP:' - - 'TODO:' - allowedPatterns: '' - customMessage: '' - ForbiddenImport: - active: false - imports: [] - forbiddenPatterns: '' - ForbiddenMethodCall: - active: false - methods: - - 'kotlin.io.print' - - 'kotlin.io.println' - ForbiddenPublicDataClass: - active: true - excludes: ['**'] - ignorePackages: - - '*.internal' - - '*.internal.*' - ForbiddenVoid: - active: false - ignoreOverridden: false - ignoreUsageInGenerics: false - FunctionOnlyReturningConstant: - active: true - ignoreOverridableFunction: true - ignoreActualFunction: true - excludedFunctions: '' - LibraryCodeMustSpecifyReturnType: - active: true - excludes: ['**'] - LibraryEntitiesShouldNotBePublic: - active: true - excludes: ['**'] - LoopWithTooManyJumpStatements: - active: true - maxJumpCount: 1 - MagicNumber: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - ignoreNumbers: - - '-1' - - '0' - - '1' - - '2' - ignoreHashCodeFunction: true - ignorePropertyDeclaration: false - ignoreLocalVariableDeclaration: false - ignoreConstantDeclaration: true - ignoreCompanionObjectPropertyDeclaration: true - ignoreAnnotation: false - ignoreNamedArgument: true - ignoreEnums: false - ignoreRanges: false - ignoreExtensionFunctions: true - MandatoryBracesIfStatements: - active: false - MandatoryBracesLoops: - active: false - MaxLineLength: - active: true - maxLineLength: 120 - excludePackageStatements: true - excludeImportStatements: true - excludeCommentStatements: false - MayBeConst: - active: true - ModifierOrder: - active: true - MultilineLambdaItParameter: - active: false - NestedClassesVisibility: - active: true - NewLineAtEndOfFile: - active: true - NoTabs: - active: false - ObjectLiteralToLambda: - active: false - OptionalAbstractKeyword: - active: true - OptionalUnit: - active: false - OptionalWhenBraces: - active: false - PreferToOverPairSyntax: - active: false - ProtectedMemberInFinalClass: - active: true - RedundantExplicitType: - active: false - RedundantHigherOrderMapUsage: - active: false - RedundantVisibilityModifierRule: - active: false - ReturnCount: - active: true - max: 2 - excludedFunctions: 'equals' - excludeLabeled: false - excludeReturnFromLambda: true - excludeGuardClauses: false - SafeCast: - active: true - SerialVersionUIDInSerializableClass: - active: true - SpacingBetweenPackageAndImports: - active: false - ThrowsCount: - active: true - max: 4 - excludeGuardClauses: true - TrailingWhitespace: - active: false - UnderscoresInNumericLiterals: - active: false - acceptableLength: 4 - UnnecessaryAbstractClass: - active: true - UnnecessaryAnnotationUseSiteTarget: - active: false - UnnecessaryApply: - active: true - UnnecessaryFilter: - active: false - UnnecessaryInheritance: - active: true - UnnecessaryLet: - active: false - UnnecessaryParentheses: - active: false - UntilInsteadOfRangeTo: - active: false - UnusedImports: - active: false - UnusedPrivateClass: - active: true - UnusedPrivateMember: - active: true - allowedNames: '(_|ignored|expected|serialVersionUID)' - UseAnyOrNoneInsteadOfFind: - active: false - UseArrayLiteralsInAnnotations: - active: false - UseCheckNotNull: - active: false - UseCheckOrError: - active: false - UseDataClass: - active: false - allowVars: false - UseEmptyCounterpart: - active: false - UseIfEmptyOrIfBlank: - active: false - UseIfInsteadOfWhen: - active: false - UseIsNullOrEmpty: - active: false - UseOrEmpty: - active: false - UseRequire: - active: false - UseRequireNotNull: - active: false - UselessCallOnNotNull: - active: true - UtilityClassWithPublicConstructor: - active: true - VarCouldBeVal: - active: true - WildcardImport: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] -# excludeImports: -# - 'java.util.*' diff --git a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/common/error/DmsException.kt b/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/common/error/DmsException.kt deleted file mode 100644 index c353c989b..000000000 --- a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/common/error/DmsException.kt +++ /dev/null @@ -1,5 +0,0 @@ -package team.aliens.dms.common.error - -abstract class DmsException( - val errorProperty: ErrorProperty -) : RuntimeException() diff --git a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/common/error/ErrorProperty.kt b/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/common/error/ErrorProperty.kt deleted file mode 100644 index 909182610..000000000 --- a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/common/error/ErrorProperty.kt +++ /dev/null @@ -1,19 +0,0 @@ -package team.aliens.dms.common.error - -interface ErrorProperty { - fun status(): Int - - fun message(): String - - fun code(): String - - fun formatMessage(vararg datas: String): ErrorProperty { - return this.let { - object : ErrorProperty { - override fun status(): Int = it.status() - override fun message(): String = it.message().format(*datas) - override fun code(): String = it.code() - } - } - } -} diff --git a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/common/error/ErrorStatus.kt b/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/common/error/ErrorStatus.kt deleted file mode 100644 index f7fc07c4e..000000000 --- a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/common/error/ErrorStatus.kt +++ /dev/null @@ -1,15 +0,0 @@ -package team.aliens.dms.common.error - -object ErrorStatus { - const val BAD_REQUEST = 400 - const val UNAUTHORIZED = 401 - const val FORBIDDEN = 403 - const val NOT_FOUND = 404 - const val CONFLICT = 409 - const val GONE = 410 - const val UNPROCESSABLE_ENTITY = 422 - const val TOO_MANY_REQUEST = 429 - const val INTERNAL_SERVER_ERROR = 500 - const val SERVICE_UNAVAILABLE = 503 - const val GATEWAY_TIMEOUT = 504 -} diff --git a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/domain/auth/model/Authority.kt b/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/domain/auth/model/Authority.kt deleted file mode 100644 index 6c14339dd..000000000 --- a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/domain/auth/model/Authority.kt +++ /dev/null @@ -1,8 +0,0 @@ -package team.aliens.dms.domain.auth.model - -enum class Authority { - STUDENT, - MANAGER, - GENERAL_TEACHER, - HEAD_TEACHER -} diff --git a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/domain/auth/model/Passport.kt b/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/domain/auth/model/Passport.kt deleted file mode 100644 index ffa2a8240..000000000 --- a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/domain/auth/model/Passport.kt +++ /dev/null @@ -1,14 +0,0 @@ -package team.aliens.dms.domain.auth.model - -import java.util.UUID - -data class Passport( - val user: PassportUser, - val userIntegrity: String -) - -data class PassportUser( - val id: UUID, - val schoolId: UUID, - val authority: Authority -) diff --git a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/domain/auth/model/UserAuthInfo.kt b/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/domain/auth/model/UserAuthInfo.kt deleted file mode 100644 index 454b4bb1e..000000000 --- a/dms-gateway/gateway-core/src/main/kotlin/team/aliens/dms/domain/auth/model/UserAuthInfo.kt +++ /dev/null @@ -1,9 +0,0 @@ -package team.aliens.dms.domain.auth.model - -import java.util.UUID - -data class UserAuthInfo( - val userId: UUID, - val schoolId: UUID, - val authority: Authority -) diff --git a/dms-gateway/gateway-infrastructure/build.gradle.kts b/dms-gateway/gateway-infrastructure/build.gradle.kts deleted file mode 100644 index 1df2cbc5a..000000000 --- a/dms-gateway/gateway-infrastructure/build.gradle.kts +++ /dev/null @@ -1,29 +0,0 @@ -plugins { - id("org.springframework.boot") version PluginVersions.SPRING_BOOT_VERSION - id("io.spring.dependency-management") version PluginVersions.DEPENDENCY_MANAGER_VERSION - kotlin("plugin.spring") version PluginVersions.SPRING_PLUGIN_VERSION -} - -dependencies { - - //core - implementation(project(":dms-gateway:gateway-core")) - - // jwt - implementation(Dependencies.JWT) - runtimeOnly(Dependencies.JWT_IMPL) - runtimeOnly(Dependencies.JWT_JACKSON) - - // Spring Cloud Gateway - implementation(Dependencies.SPRING_CLOUD_GATEWAY) - - // validation - implementation(Dependencies.SPRING_VALIDATION) - - // configuration - kapt(Dependencies.CONFIGURATION_PROCESSOR) -} - -tasks.getByName("jar") { - enabled = false -} \ No newline at end of file diff --git a/dms-gateway/gateway-infrastructure/config/detekt/detekt.yml b/dms-gateway/gateway-infrastructure/config/detekt/detekt.yml deleted file mode 100644 index db8926833..000000000 --- a/dms-gateway/gateway-infrastructure/config/detekt/detekt.yml +++ /dev/null @@ -1,810 +0,0 @@ -build: - maxIssues: -1 - excludeCorrectable: false - weights: - # complexity: 2 - # LongParameterList: 1 - # style: 1 - # comments: 1 - -config: - validation: true - warningsAsErrors: false - # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' - excludes: '' - -processors: - active: true - exclude: - - 'DetektProgressListener' - # - 'KtFileCountProcessor' - # - 'PackageCountProcessor' - # - 'ClassCountProcessor' - # - 'FunctionCountProcessor' - # - 'PropertyCountProcessor' - # - 'ProjectComplexityProcessor' - # - 'ProjectCognitiveComplexityProcessor' - # - 'ProjectLLOCProcessor' - # - 'ProjectCLOCProcessor' - # - 'ProjectLOCProcessor' - # - 'ProjectSLOCProcessor' - # - 'LicenseHeaderLoaderExtension' - -console-reports: - active: true - exclude: - - 'ProjectStatisticsReport' - - 'ComplexityReport' - - 'NotificationReport' - # - 'FindingsReport' - - 'FileBasedFindingsReport' - - 'LiteFindingsReport' - -output-reports: - active: true - exclude: - # - 'TxtOutputReport' - # - 'XmlOutputReport' - # - 'HtmlOutputReport' - -comments: - active: true - AbsentOrWrongFileLicense: - active: false - licenseTemplateFile: 'license.template' - licenseTemplateIsRegex: false - CommentOverPrivateFunction: - active: false - CommentOverPrivateProperty: - active: false - DeprecatedBlockTag: - active: false - EndOfSentenceFormat: - active: false - endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' - OutdatedDocumentation: - active: false - matchTypeParameters: true - matchDeclarationsOrder: true - UndocumentedPublicClass: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - searchInNestedClass: true - searchInInnerClass: true - searchInInnerObject: true - searchInInnerInterface: true - UndocumentedPublicFunction: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - UndocumentedPublicProperty: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - -complexity: - active: true - ComplexCondition: - active: true - threshold: 4 - ComplexInterface: - active: false - threshold: 10 - includeStaticDeclarations: false - includePrivateDeclarations: false - ComplexMethod: - active: true - threshold: 15 - ignoreSingleWhenExpression: false - ignoreSimpleWhenEntries: false - ignoreNestingFunctions: false - nestingFunctions: - - 'also' - - 'apply' - - 'forEach' - - 'isNotNull' - - 'ifNull' - - 'let' - - 'run' - - 'use' - - 'with' - LabeledExpression: - active: false - ignoredLabels: [] - LargeClass: - active: true - threshold: 600 - LongMethod: - active: true - threshold: 60 - LongParameterList: - active: true - functionThreshold: 6 - constructorThreshold: 7 - ignoreDefaultParameters: false - ignoreDataClasses: true - ignoreAnnotatedParameter: [] - excludes: ['**/vo/**', '**/usecase/**'] - MethodOverloading: - active: false - threshold: 6 - NamedArguments: - active: false - threshold: 3 - NestedBlockDepth: - active: true - threshold: 4 - ReplaceSafeCallChainWithRun: - active: false - StringLiteralDuplication: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - threshold: 3 - ignoreAnnotation: true - excludeStringsWithLessThan5Characters: true - ignoreStringsRegex: '$^' - TooManyFunctions: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - thresholdInFiles: 11 - thresholdInClasses: 11 - thresholdInInterfaces: 11 - thresholdInObjects: 11 - thresholdInEnums: 11 - ignoreDeprecated: false - ignorePrivate: false - ignoreOverridden: false - -coroutines: - active: true - GlobalCoroutineUsage: - active: false - InjectDispatcher: - active: false - dispatcherNames: - - 'IO' - - 'Default' - - 'Unconfined' - RedundantSuspendModifier: - active: false - SleepInsteadOfDelay: - active: false - SuspendFunWithFlowReturnType: - active: false - -empty-blocks: - active: true - EmptyCatchBlock: - active: true - allowedExceptionNameRegex: '_|(ignore|expected).*' - EmptyClassBlock: - active: true - EmptyDefaultConstructor: - active: true - EmptyDoWhileBlock: - active: true - EmptyElseBlock: - active: true - EmptyFinallyBlock: - active: true - EmptyForBlock: - active: true - EmptyFunctionBlock: - active: true - ignoreOverridden: false - EmptyIfBlock: - active: true - EmptyInitBlock: - active: true - EmptyKtFile: - active: true - EmptySecondaryConstructor: - active: true - EmptyTryBlock: - active: true - EmptyWhenBlock: - active: true - EmptyWhileBlock: - active: true - -exceptions: - active: true - ExceptionRaisedInUnexpectedLocation: - active: true - methodNames: - - 'equals' - - 'finalize' - - 'hashCode' - - 'toString' - InstanceOfCheckForException: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - NotImplementedDeclaration: - active: false - ObjectExtendsThrowable: - active: false - PrintStackTrace: - active: true - RethrowCaughtException: - active: true - ReturnFromFinally: - active: true - ignoreLabeled: false - SwallowedException: - active: true - ignoredExceptionTypes: - - 'InterruptedException' - - 'MalformedURLException' - - 'NumberFormatException' - - 'ParseException' - allowedExceptionNameRegex: '_|(ignore|expected).*' - ThrowingExceptionFromFinally: - active: true - ThrowingExceptionInMain: - active: false - ThrowingExceptionsWithoutMessageOrCause: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - exceptions: - - 'ArrayIndexOutOfBoundsException' - - 'Exception' - - 'IllegalArgumentException' - - 'IllegalMonitorStateException' - - 'IllegalStateException' - - 'IndexOutOfBoundsException' - - 'NullPointerException' - - 'RuntimeException' - - 'Throwable' - ThrowingNewInstanceOfSameException: - active: true - TooGenericExceptionCaught: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - exceptionNames: - - 'ArrayIndexOutOfBoundsException' - - 'Error' - - 'Exception' - - 'IllegalMonitorStateException' - - 'IndexOutOfBoundsException' - - 'NullPointerException' - - 'RuntimeException' - - 'Throwable' - allowedExceptionNameRegex: '_|(ignore|expected).*' - TooGenericExceptionThrown: - active: true - exceptionNames: - - 'Error' - - 'Exception' - - 'RuntimeException' - - 'Throwable' - -formatting: - active: true - android: false - autoCorrect: true - excludes: ['**/SecurityConfig.kt'] - AnnotationOnSeparateLine: - active: false - autoCorrect: true - AnnotationSpacing: - active: false - autoCorrect: true - ArgumentListWrapping: - active: false - autoCorrect: true - indentSize: 4 - maxLineLength: 120 - ChainWrapping: - active: true - autoCorrect: true - CommentSpacing: - active: true - autoCorrect: true - EnumEntryNameCase: - active: false - autoCorrect: true - Filename: - active: true - FinalNewline: - active: true - autoCorrect: true - insertFinalNewLine: true - ImportOrdering: - active: true - autoCorrect: true - layout: '*,java.**,javax.**,kotlin.**,^' - Indentation: - active: true - autoCorrect: true - indentSize: 4 - continuationIndentSize: 4 - MaximumLineLength: - active: true - maxLineLength: 120 - ignoreBackTickedIdentifier: false - ModifierOrdering: - active: true - autoCorrect: true - MultiLineIfElse: - active: false - autoCorrect: true - NoBlankLineBeforeRbrace: - active: true - autoCorrect: true - NoConsecutiveBlankLines: - active: true - autoCorrect: true - NoEmptyClassBody: - active: true - autoCorrect: true - NoEmptyFirstLineInMethodBlock: - active: false - autoCorrect: true - NoLineBreakAfterElse: - active: true - autoCorrect: true - NoLineBreakBeforeAssignment: - active: true - autoCorrect: true - NoMultipleSpaces: - active: true - autoCorrect: true - NoSemicolons: - active: false - autoCorrect: true - NoTrailingSpaces: - active: true - autoCorrect: true - NoUnitReturn: - active: true - autoCorrect: true - NoUnusedImports: - active: true - autoCorrect: true - NoWildcardImports: - active: true - PackageName: - active: false - autoCorrect: true - ParameterListWrapping: - active: true - autoCorrect: true - indentSize: 4 - maxLineLength: 120 - SpacingAroundAngleBrackets: - active: false - autoCorrect: true - SpacingAroundColon: - active: true - autoCorrect: true - SpacingAroundComma: - active: true - autoCorrect: true - SpacingAroundCurly: - active: true - autoCorrect: true - SpacingAroundDot: - active: true - autoCorrect: true - SpacingAroundDoubleColon: - active: false - autoCorrect: true - SpacingAroundKeyword: - active: true - autoCorrect: true - SpacingAroundOperators: - active: true - autoCorrect: true - SpacingAroundParens: - active: true - autoCorrect: true - SpacingAroundRangeOperator: - active: true - autoCorrect: true - SpacingAroundUnaryOperator: - active: false - autoCorrect: true - SpacingBetweenDeclarationsWithAnnotations: - active: false - autoCorrect: true - SpacingBetweenDeclarationsWithComments: - active: false - autoCorrect: true - StringTemplate: - active: true - autoCorrect: true - -naming: - active: true - BooleanPropertyNaming: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - allowedPattern: '^(is|has|are)' - ClassNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - classPattern: '[A-Z][a-zA-Z0-9]*' - ConstructorParameterNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - parameterPattern: '[a-z][A-Za-z0-9]*' - privateParameterPattern: '[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' - ignoreOverridden: true - EnumNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' - ForbiddenClassName: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - forbiddenName: [] - FunctionMaxLength: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - maximumFunctionNameLength: 30 - FunctionMinLength: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - minimumFunctionNameLength: 3 - FunctionNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)' - excludeClassPattern: '$^' - ignoreOverridden: true - FunctionParameterNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - parameterPattern: '[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' - ignoreOverridden: true - InvalidPackageDeclaration: - active: false - rootPackage: '' - LambdaParameterNaming: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - parameterPattern: '[a-z][A-Za-z0-9]*|_' - MatchingDeclarationName: - active: true - mustBeFirst: true - MemberNameEqualsClassName: - active: true - ignoreOverridden: true - NoNameShadowing: - active: false - NonBooleanPropertyPrefixedWithIs: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - ObjectPropertyNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - constantPattern: '[A-Za-z][_A-Za-z0-9]*' - propertyPattern: '[A-Za-z][_A-Za-z0-9]*' - privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' - PackageNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' - TopLevelPropertyNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - constantPattern: '[A-Z][_A-Z0-9]*' - propertyPattern: '[A-Za-z][_A-Za-z0-9]*' - privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' - VariableMaxLength: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - maximumVariableNameLength: 64 - VariableMinLength: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - minimumVariableNameLength: 1 - VariableNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - variablePattern: '[a-z][A-Za-z0-9]*' - privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' - ignoreOverridden: true - -performance: - active: true - ArrayPrimitive: - active: true - ForEachOnRange: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - SpreadOperator: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - UnnecessaryTemporaryInstantiation: - active: true - -potential-bugs: - active: true - AvoidReferentialEquality: - active: false - forbiddenTypePatterns: - - 'kotlin.String' - CastToNullableType: - active: false - Deprecation: - active: false - DontDowncastCollectionTypes: - active: false - DoubleMutabilityForCollection: - active: false - DuplicateCaseInWhenExpression: - active: true - EqualsAlwaysReturnsTrueOrFalse: - active: true - EqualsWithHashCodeExist: - active: true - ExitOutsideMain: - active: false - ExplicitGarbageCollectionCall: - active: true - HasPlatformType: - active: false - IgnoredReturnValue: - active: false - restrictToAnnotatedMethods: true - returnValueAnnotations: - - '*.CheckResult' - - '*.CheckReturnValue' - ignoreReturnValueAnnotations: - - '*.CanIgnoreReturnValue' - ImplicitDefaultLocale: - active: true - ImplicitUnitReturnType: - active: false - allowExplicitReturnType: true - InvalidRange: - active: true - IteratorHasNextCallsNextMethod: - active: true - IteratorNotThrowingNoSuchElementException: - active: true - LateinitUsage: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - ignoreOnClassesPattern: '' - MapGetWithNotNullAssertionOperator: - active: false - MissingPackageDeclaration: - active: false - excludes: ['**/*.kts'] - MissingWhenCase: - active: true - allowElseExpression: true - NullableToStringCall: - active: false - RedundantElseInWhen: - active: true - UnconditionalJumpStatementInLoop: - active: false - UnnecessaryNotNullOperator: - active: true - UnnecessarySafeCall: - active: true - UnreachableCatchBlock: - active: false - UnreachableCode: - active: true - UnsafeCallOnNullableType: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - UnsafeCast: - active: true - UnusedUnaryOperator: - active: false - UselessPostfixExpression: - active: false - WrongEqualsTypeParameter: - active: true - -style: - active: true - ClassOrdering: - active: false - CollapsibleIfStatements: - active: false - DataClassContainsFunctions: - active: false - conversionFunctionPrefix: 'to' - DataClassShouldBeImmutable: - active: false - DestructuringDeclarationWithTooManyEntries: - active: false - maxDestructuringEntries: 3 - EqualsNullCall: - active: true - EqualsOnSignatureLine: - active: false - ExplicitCollectionElementAccessMethod: - active: false - ExplicitItLambdaParameter: - active: false - ExpressionBodySyntax: - active: false - includeLineWrapping: false - ForbiddenComment: - active: true - values: - - 'FIXME:' - - 'STOPSHIP:' - - 'TODO:' - allowedPatterns: '' - customMessage: '' - ForbiddenImport: - active: false - imports: [] - forbiddenPatterns: '' - ForbiddenMethodCall: - active: false - methods: - - 'kotlin.io.print' - - 'kotlin.io.println' - ForbiddenPublicDataClass: - active: true - excludes: ['**'] - ignorePackages: - - '*.internal' - - '*.internal.*' - ForbiddenVoid: - active: false - ignoreOverridden: false - ignoreUsageInGenerics: false - FunctionOnlyReturningConstant: - active: true - ignoreOverridableFunction: true - ignoreActualFunction: true - excludedFunctions: '' - LibraryCodeMustSpecifyReturnType: - active: true - excludes: ['**'] - LibraryEntitiesShouldNotBePublic: - active: true - excludes: ['**'] - LoopWithTooManyJumpStatements: - active: true - maxJumpCount: 1 - MagicNumber: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - ignoreNumbers: - - '-1' - - '0' - - '1' - - '2' - ignoreHashCodeFunction: true - ignorePropertyDeclaration: false - ignoreLocalVariableDeclaration: false - ignoreConstantDeclaration: true - ignoreCompanionObjectPropertyDeclaration: true - ignoreAnnotation: false - ignoreNamedArgument: true - ignoreEnums: false - ignoreRanges: false - ignoreExtensionFunctions: true - MandatoryBracesIfStatements: - active: false - MandatoryBracesLoops: - active: false - MaxLineLength: - active: true - maxLineLength: 120 - excludePackageStatements: true - excludeImportStatements: true - excludeCommentStatements: false - MayBeConst: - active: true - ModifierOrder: - active: true - MultilineLambdaItParameter: - active: false - NestedClassesVisibility: - active: true - NewLineAtEndOfFile: - active: true - NoTabs: - active: false - ObjectLiteralToLambda: - active: false - OptionalAbstractKeyword: - active: true - OptionalUnit: - active: false - OptionalWhenBraces: - active: false - PreferToOverPairSyntax: - active: false - ProtectedMemberInFinalClass: - active: true - RedundantExplicitType: - active: false - RedundantHigherOrderMapUsage: - active: false - RedundantVisibilityModifierRule: - active: false - ReturnCount: - active: true - max: 2 - excludedFunctions: 'equals' - excludeLabeled: false - excludeReturnFromLambda: true - excludeGuardClauses: false - SafeCast: - active: true - SerialVersionUIDInSerializableClass: - active: true - SpacingBetweenPackageAndImports: - active: false - ThrowsCount: - active: true - max: 4 - excludeGuardClauses: true - TrailingWhitespace: - active: false - UnderscoresInNumericLiterals: - active: false - acceptableLength: 4 - UnnecessaryAbstractClass: - active: true - UnnecessaryAnnotationUseSiteTarget: - active: false - UnnecessaryApply: - active: true - UnnecessaryFilter: - active: false - UnnecessaryInheritance: - active: true - UnnecessaryLet: - active: false - UnnecessaryParentheses: - active: false - UntilInsteadOfRangeTo: - active: false - UnusedImports: - active: false - UnusedPrivateClass: - active: true - UnusedPrivateMember: - active: true - allowedNames: '(_|ignored|expected|serialVersionUID)' - UseAnyOrNoneInsteadOfFind: - active: false - UseArrayLiteralsInAnnotations: - active: false - UseCheckNotNull: - active: false - UseCheckOrError: - active: false - UseDataClass: - active: false - allowVars: false - UseEmptyCounterpart: - active: false - UseIfEmptyOrIfBlank: - active: false - UseIfInsteadOfWhen: - active: false - UseIsNullOrEmpty: - active: false - UseOrEmpty: - active: false - UseRequire: - active: false - UseRequireNotNull: - active: false - UselessCallOnNotNull: - active: true - UtilityClassWithPublicConstructor: - active: true - VarCouldBeVal: - active: true - WildcardImport: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] -# excludeImports: -# - 'java.util.*' diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/DmsGatewayApplication.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/DmsGatewayApplication.kt deleted file mode 100644 index f9434a524..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/DmsGatewayApplication.kt +++ /dev/null @@ -1,11 +0,0 @@ -package team.aliens.dms - -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.runApplication - -@SpringBootApplication -class DmsGatewayApplication - -fun main(args: Array) { - runApplication(*args) -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/JacksonConfig.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/JacksonConfig.kt deleted file mode 100644 index 1bb0b522e..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/JacksonConfig.kt +++ /dev/null @@ -1,24 +0,0 @@ -package team.aliens.dms.global.config - -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.databind.PropertyNamingStrategies -import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer -import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer -import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder -import java.time.format.DateTimeFormatter - -@Configuration -class JacksonConfig { - @Bean - fun jackson2ObjectMapperBuilder(): Jackson2ObjectMapperBuilder { - return Jackson2ObjectMapperBuilder() - .serializers(LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))) - .serializers(LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm"))) - .serializers(LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))) - .propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE) - .serializationInclusion(JsonInclude.Include.NON_NULL) - } -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/PropertiesScanConfig.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/PropertiesScanConfig.kt deleted file mode 100644 index 12bb6cf26..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/PropertiesScanConfig.kt +++ /dev/null @@ -1,15 +0,0 @@ -package team.aliens.dms.global.config - -import org.springframework.boot.context.properties.ConfigurationPropertiesScan -import org.springframework.context.annotation.Configuration -import team.aliens.dms.global.config.properties.ServicesProperties -import team.aliens.dms.global.security.SecurityProperties - -@ConfigurationPropertiesScan( - basePackageClasses = [ - SecurityProperties::class, - ServicesProperties::class - ] -) -@Configuration -class PropertiesScanConfig diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/RouteConfig.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/RouteConfig.kt deleted file mode 100644 index 02fe12355..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/RouteConfig.kt +++ /dev/null @@ -1,32 +0,0 @@ -package team.aliens.dms.global.config - -import org.springframework.cloud.gateway.route.RouteLocator -import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import team.aliens.dms.global.config.properties.ServicesProperties -import team.aliens.dms.global.filter.AuthorizationHeaderGatewayFilterFactory - -@Configuration -class RouteConfig( - private val authorizationHeaderGatewayFilterFactory: AuthorizationHeaderGatewayFilterFactory, - private val servicesProperties: ServicesProperties -) { - - @Bean - fun customRouteLocator(builder: RouteLocatorBuilder): RouteLocator { - return builder.routes() - .route("dms-main") { r -> - r.path("/**") - .filters { f -> - f.filter( - authorizationHeaderGatewayFilterFactory.apply( - AuthorizationHeaderGatewayFilterFactory.Config() - ) - ) - } - .uri(servicesProperties.mainUrl) - } - .build() - } -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/properties/ServicesProperties.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/properties/ServicesProperties.kt deleted file mode 100644 index ad95016b9..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/config/properties/ServicesProperties.kt +++ /dev/null @@ -1,10 +0,0 @@ -package team.aliens.dms.global.config.properties - -import org.springframework.boot.context.properties.ConfigurationProperties - -@ConfigurationProperties(prefix = "app.services") -class ServicesProperties( - main: String, -) { - val mainUrl: String = main -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GatewayGlobalExceptionHandler.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GatewayGlobalExceptionHandler.kt deleted file mode 100644 index 4b9e921b9..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GatewayGlobalExceptionHandler.kt +++ /dev/null @@ -1,53 +0,0 @@ -package team.aliens.dms.global.error - -import com.fasterxml.jackson.databind.ObjectMapper -import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler -import org.springframework.core.annotation.Order -import org.springframework.core.io.buffer.DataBuffer -import org.springframework.http.MediaType -import org.springframework.stereotype.Component -import org.springframework.web.server.ResponseStatusException -import org.springframework.web.server.ServerWebExchange -import reactor.core.publisher.Mono -import team.aliens.dms.common.error.DmsException -import java.nio.charset.StandardCharsets - -@Component -@Order(-2) -class GatewayGlobalExceptionHandler( - private val objectMapper: ObjectMapper -) : ErrorWebExceptionHandler { - - override fun handle( - exchange: ServerWebExchange, - ex: Throwable - ): Mono { - - val errorCode = when (ex) { - is DmsException -> ex.errorProperty - is ResponseStatusException -> { - when (ex.statusCode.value()) { - 400 -> GlobalErrorCode.BAD_REQUEST - else -> GlobalErrorCode.INTERNAL_SERVER_ERROR - } - } - else -> GlobalErrorCode.INTERNAL_SERVER_ERROR - } - - exchange.response.apply { - this.setRawStatusCode(errorCode.status()) - this.headers.contentType = MediaType("application", "json", StandardCharsets.UTF_8) - - val errorResponse = mapOf( - "status" to errorCode.status(), - "message" to errorCode.message(), - "code" to errorCode.code() - ) - - val responseBody = objectMapper.writeValueAsString(errorResponse) - val buffer: DataBuffer = bufferFactory().wrap(responseBody.toByteArray()) - - return writeWith(Mono.just(buffer)) - } - } -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GlobalErrorCode.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GlobalErrorCode.kt deleted file mode 100644 index c060daaba..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GlobalErrorCode.kt +++ /dev/null @@ -1,20 +0,0 @@ -package team.aliens.dms.global.error - -import team.aliens.dms.common.error.ErrorProperty -import team.aliens.dms.common.error.ErrorStatus - -enum class GlobalErrorCode( - private val status: Int, - private val message: String, - private val sequence: Int -) : ErrorProperty { - - BAD_REQUEST(ErrorStatus.BAD_REQUEST, "Bad Request", 1), - - INTERNAL_SERVER_ERROR(ErrorStatus.INTERNAL_SERVER_ERROR, "Internal Server Error", 1) - ; - - override fun status(): Int = status - override fun message(): String = message - override fun code(): String = "GLOBAL-$status-$sequence" -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GlobalExceptions.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GlobalExceptions.kt deleted file mode 100644 index fcbeb2376..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GlobalExceptions.kt +++ /dev/null @@ -1,7 +0,0 @@ -package team.aliens.dms.global.error - -import team.aliens.dms.common.error.DmsException - -object InternalServerError : DmsException( - GlobalErrorCode.INTERNAL_SERVER_ERROR -) diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/AuthorizationHeaderGatewayFilterFactory.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/AuthorizationHeaderGatewayFilterFactory.kt deleted file mode 100644 index 201b202cc..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/AuthorizationHeaderGatewayFilterFactory.kt +++ /dev/null @@ -1,71 +0,0 @@ -package team.aliens.dms.global.filter - -import com.fasterxml.jackson.databind.ObjectMapper -import org.springframework.cloud.gateway.filter.GatewayFilter -import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory -import org.springframework.stereotype.Component -import org.springframework.util.AntPathMatcher -import org.springframework.web.server.ServerWebExchange -import reactor.core.publisher.Mono -import team.aliens.dms.domain.auth.model.Passport -import team.aliens.dms.global.security.SecurityPaths -import team.aliens.dms.global.security.exception.InvalidTokenException -import team.aliens.dms.global.security.passport.PassportAdapter -import team.aliens.dms.global.security.passport.PassportSecurityProperties -import team.aliens.dms.global.security.token.JwtProperties - -@Component -class AuthorizationHeaderGatewayFilterFactory( - private val objectMapper: ObjectMapper, - private val passportAdapter: PassportAdapter -) : AbstractGatewayFilterFactory(Config::class.java) { - - private val pathMatcher = AntPathMatcher() - - override fun apply(config: Config): GatewayFilter { - return GatewayFilter { exchange, chain -> - val requestPath = exchange.request.uri.path - val isPermitAllPath = SecurityPaths.PERMIT_ALL_PATHS.any { pathMatcher.match(it, requestPath) } - - if (isPermitAllPath) { - return@GatewayFilter chain.filter(exchange) - } - - Mono.fromCallable { resolveToken(exchange) } - .flatMap { token -> - passportAdapter.generatePassportByToken(token) - } - .flatMap { passport -> - Mono.fromCallable { serializePassport(passport) } - .map { serializedPassport -> - val modifiedExchange = exchange.mutate() - .request { - it.header(PassportSecurityProperties.PASSPORT_HEADER, serializedPassport) - } - .build() - modifiedExchange - } - .flatMap { modifiedExchange -> - chain.filter(modifiedExchange) - } - } - } - } - - private fun resolveToken(exchange: ServerWebExchange): String { - val authorizationHeader = exchange.request.headers.getFirst(JwtProperties.HEADER) - ?: throw InvalidTokenException - - if (!authorizationHeader.startsWith(JwtProperties.PREFIX)) { - throw InvalidTokenException - } - - return authorizationHeader.removePrefix(JwtProperties.PREFIX) - } - - private fun serializePassport(passport: Passport): String { - return objectMapper.writeValueAsString(passport) - } - - class Config -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityPaths.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityPaths.kt deleted file mode 100644 index 11c0c68d7..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityPaths.kt +++ /dev/null @@ -1,42 +0,0 @@ -package team.aliens.dms.global.security - -object SecurityPaths { - - val PERMIT_ALL_PATHS = listOf( - // healthcheck - "/", - - // auth - "/auth/account-id", - "/auth/email", - "/auth/code", - "/auth/tokens", - "/auth/reissue", - "/auth/passport", - - // students - "/students/email/duplication", - "/students/account-id/duplication", - "/students/account-id/{school-id}", - "/students/name", - "/students/signup", - "/students/password/initialization", - - // managers - "/managers/account-id/{school-id}", - "/managers/password/initialization", - - // schools - "/schools", - "/schools/question/{school-id}", - "/schools/answer/{school-id}", - "/schools/code", - - // files - "/files", - "/files/url", - - // templates - "/templates", - ) -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityProperties.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityProperties.kt deleted file mode 100644 index 9c5713772..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityProperties.kt +++ /dev/null @@ -1,16 +0,0 @@ -package team.aliens.dms.global.security - -import io.jsonwebtoken.security.Keys -import org.springframework.boot.context.properties.ConfigurationProperties -import java.util.Base64 -import javax.crypto.SecretKey - -@ConfigurationProperties(prefix = "secret") -class SecurityProperties( - secretKey: String, -) { - val secretKey: SecretKey = Keys.hmacShaKeyFor( - Base64.getEncoder().encodeToString(secretKey.toByteArray()) - .toByteArray(Charsets.UTF_8) - ) -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/exception/ExpiredTokenException.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/exception/ExpiredTokenException.kt deleted file mode 100644 index ba06b8dfd..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/exception/ExpiredTokenException.kt +++ /dev/null @@ -1,8 +0,0 @@ -package team.aliens.dms.global.security.exception - -import team.aliens.dms.common.error.DmsException -import team.aliens.dms.global.security.exception.error.SecurityErrorCode - -object ExpiredTokenException : DmsException( - SecurityErrorCode.EXPIRED_TOKEN -) diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/exception/InvalidTokenException.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/exception/InvalidTokenException.kt deleted file mode 100644 index 902fa8bd0..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/exception/InvalidTokenException.kt +++ /dev/null @@ -1,8 +0,0 @@ -package team.aliens.dms.global.security.exception - -import team.aliens.dms.common.error.DmsException -import team.aliens.dms.global.security.exception.error.SecurityErrorCode - -object InvalidTokenException : DmsException( - SecurityErrorCode.INVALID_TOKEN -) diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/exception/error/SecurityErrorCode.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/exception/error/SecurityErrorCode.kt deleted file mode 100644 index 85f7a5db0..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/exception/error/SecurityErrorCode.kt +++ /dev/null @@ -1,19 +0,0 @@ -package team.aliens.dms.global.security.exception.error - -import team.aliens.dms.common.error.ErrorProperty -import team.aliens.dms.common.error.ErrorStatus - -enum class SecurityErrorCode( - private val status: Int, - private val message: String, - private val sequence: Int -) : ErrorProperty { - - INVALID_TOKEN(ErrorStatus.UNAUTHORIZED, "Invalid Token", 1), - EXPIRED_TOKEN(ErrorStatus.UNAUTHORIZED, "Expired Token", 2) - ; - - override fun status(): Int = status - override fun message(): String = message - override fun code(): String = "SECURITY-$status-$sequence" -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportAdapter.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportAdapter.kt deleted file mode 100644 index 8240a0250..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportAdapter.kt +++ /dev/null @@ -1,32 +0,0 @@ -package team.aliens.dms.global.security.passport - -import org.springframework.stereotype.Component -import reactor.core.publisher.Mono -import team.aliens.dms.domain.auth.model.Passport -import team.aliens.dms.domain.auth.model.PassportUser -import team.aliens.dms.global.security.token.JwtParser - -@Component -class PassportAdapter( - private val jwtParser: JwtParser, - private val passportIntegrityGenerator: PassportIntegrityGenerator -) { - - fun generatePassportByToken(token: String): Mono { - return jwtParser.extractUserInfo(token) - .map { userInfo -> - val passportUser = PassportUser( - id = userInfo.userId, - schoolId = userInfo.schoolId, - authority = userInfo.authority - ) - - val userIntegrity = passportIntegrityGenerator.generate(passportUser) - - Passport( - user = passportUser, - userIntegrity = userIntegrity - ) - } - } -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportIntegrityGenerator.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportIntegrityGenerator.kt deleted file mode 100644 index 4fa9d8032..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportIntegrityGenerator.kt +++ /dev/null @@ -1,21 +0,0 @@ -package team.aliens.dms.global.security.passport - -import org.springframework.stereotype.Component -import team.aliens.dms.domain.auth.model.PassportUser -import java.util.Base64 -import javax.crypto.Mac -import javax.crypto.spec.SecretKeySpec - -@Component -class PassportIntegrityGenerator( - private val passportProperties: PassportProperties -) { - fun generate(passportUser: PassportUser): String { - val data = "${passportUser.id}|${passportUser.schoolId}|${passportUser.authority}" - val hmacKey = SecretKeySpec(passportProperties.secretKey.encoded, PassportSecurityProperties.HMAC_ALGORITHM) - val mac = Mac.getInstance(PassportSecurityProperties.HMAC_ALGORITHM) - mac.init(hmacKey) - val hmacBytes = mac.doFinal(data.toByteArray()) - return Base64.getEncoder().encodeToString(hmacBytes) - } -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportProperties.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportProperties.kt deleted file mode 100644 index 05929481e..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportProperties.kt +++ /dev/null @@ -1,14 +0,0 @@ -package team.aliens.dms.global.security.passport - -import io.jsonwebtoken.security.Keys -import org.springframework.boot.context.properties.ConfigurationProperties -import javax.crypto.SecretKey - -@ConfigurationProperties(prefix = "passport") -class PassportProperties( - secretKey: String -) { - val secretKey: SecretKey = Keys.hmacShaKeyFor( - secretKey.toByteArray(Charsets.UTF_8) - ) -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportSecurityProperties.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportSecurityProperties.kt deleted file mode 100644 index 809a33db5..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportSecurityProperties.kt +++ /dev/null @@ -1,6 +0,0 @@ -package team.aliens.dms.global.security.passport - -object PassportSecurityProperties { - const val PASSPORT_HEADER = "X-PASSPORT" - const val HMAC_ALGORITHM = "HmacSHA256" -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/token/JwtParser.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/token/JwtParser.kt deleted file mode 100644 index e13296af4..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/token/JwtParser.kt +++ /dev/null @@ -1,78 +0,0 @@ -package team.aliens.dms.global.security.token - -import io.jsonwebtoken.Claims -import io.jsonwebtoken.ExpiredJwtException -import io.jsonwebtoken.Header -import io.jsonwebtoken.InvalidClaimException -import io.jsonwebtoken.Jws -import io.jsonwebtoken.JwtException -import io.jsonwebtoken.Jwts -import org.springframework.stereotype.Component -import reactor.core.publisher.Mono -import team.aliens.dms.domain.auth.model.Authority -import team.aliens.dms.domain.auth.model.UserAuthInfo -import team.aliens.dms.global.error.InternalServerError -import team.aliens.dms.global.security.SecurityProperties -import team.aliens.dms.global.security.exception.ExpiredTokenException -import team.aliens.dms.global.security.exception.InvalidTokenException -import java.util.UUID - -@Component -class JwtParser( - private val securityProperties: SecurityProperties -) { - - fun extractUserInfo(token: String): Mono { - - return getClaims(token) - .flatMap { claims -> - - if (claims.header[Header.TYPE] != JwtProperties.ACCESS) { - Mono.error(InvalidTokenException) - } else { - parseUserInfo(claims.body) - } - } - } - - private fun getClaims(token: String): Mono> { - return try { - val claims = Jwts.parserBuilder() - .setSigningKey(securityProperties.secretKey) - .build() - .parseClaimsJws(token) - - Mono.just(claims) - } catch (e: Exception) { - val exception = when (e) { - is InvalidClaimException -> InvalidTokenException - is ExpiredJwtException -> ExpiredTokenException - is JwtException -> InvalidTokenException - else -> InternalServerError - } - Mono.error(exception) - } - } - - private fun parseUserInfo(claims: Claims): Mono { - return try { - - val authority = claims.get(JwtProperties.AUTHORITY, String::class.java) - - val validAuthority = try { - Authority.valueOf(authority) - } catch (e: IllegalArgumentException) { - return Mono.error(InvalidTokenException) - } - - val userInfo = UserAuthInfo( - userId = UUID.fromString(claims.id), - schoolId = UUID.fromString(claims.get(JwtProperties.SCHOOL_ID, String::class.java)), - authority = validAuthority - ) - Mono.just(userInfo) - } catch (e: Exception) { - Mono.error(InvalidTokenException) - } - } -} diff --git a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/token/JwtProperties.kt b/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/token/JwtProperties.kt deleted file mode 100644 index feba2d29e..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/kotlin/team/aliens/dms/global/security/token/JwtProperties.kt +++ /dev/null @@ -1,9 +0,0 @@ -package team.aliens.dms.global.security.token - -object JwtProperties { - const val HEADER = "Authorization" - const val PREFIX = "Bearer " - const val ACCESS = "access" - const val AUTHORITY = "authority" - const val SCHOOL_ID = "school_id" -} diff --git a/dms-gateway/gateway-infrastructure/src/main/resources/application-dev.yml b/dms-gateway/gateway-infrastructure/src/main/resources/application-dev.yml deleted file mode 100644 index 7b8fdc591..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/resources/application-dev.yml +++ /dev/null @@ -1,4 +0,0 @@ -spring: - config: - activate: - on-profile: dev \ No newline at end of file diff --git a/dms-gateway/gateway-infrastructure/src/main/resources/application-local.yml b/dms-gateway/gateway-infrastructure/src/main/resources/application-local.yml deleted file mode 100644 index eb2416fab..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/resources/application-local.yml +++ /dev/null @@ -1,4 +0,0 @@ -spring: - config: - activate: - on-profile: local \ No newline at end of file diff --git a/dms-gateway/gateway-infrastructure/src/main/resources/application-prod.yml b/dms-gateway/gateway-infrastructure/src/main/resources/application-prod.yml deleted file mode 100644 index e9d7bc7db..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/resources/application-prod.yml +++ /dev/null @@ -1,4 +0,0 @@ -spring: - config: - activate: - on-profile: prod \ No newline at end of file diff --git a/dms-gateway/gateway-infrastructure/src/main/resources/application.yml b/dms-gateway/gateway-infrastructure/src/main/resources/application.yml deleted file mode 100644 index 22cc29e4d..000000000 --- a/dms-gateway/gateway-infrastructure/src/main/resources/application.yml +++ /dev/null @@ -1,43 +0,0 @@ -server: - port: 8080 - -spring: - application: - name: dms-gateway - cloud: - gateway: - globalcors: - cors-configurations: - '[/**]': - allowedOrigins: - - "http://localhost:3000" - - "http://localhost:3001" - - "http://localhost:3002" - - "https://admin-dms.dsmhs.kr" - - "https://admin-dev-dms.dsmhs.kr" - - "https://webview-dms.dsmhs.kr" - - "https://head-teacher-dms.dsmhs.kr" - - "https://general-teacher-dms.dsmhs.kr" - - "https://student-dms.dsmhs.kr" - allowedMethods: - - GET - - POST - - PUT - - DELETE - - PATCH - - OPTIONS - allowedHeaders: "*" - allowCredentials: true - exposedHeaders: - - "Content-Disposition" - -app: - services: - main: ${MAIN_SERVICE_URL:http://localhost:8081} - -secret: - secret-key: ${SECRET_KEY:asdfghgfdsasdfghgfdsasdfghgfdsasdfghgfdsasdfghgfdsasdfghgfdsasdfghgfdsasdfghgfdsasdfghgfdsasdfghgfds} - -passport: - secret-key: ${PASSPORT_SECRET_KEY:qwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiop} - diff --git a/dms-gateway/gateway-infrastructure/src/test/kotlin/team/aliens/GatewayApplicationTests.kt b/dms-gateway/gateway-infrastructure/src/test/kotlin/team/aliens/GatewayApplicationTests.kt deleted file mode 100644 index 47ff20e63..000000000 --- a/dms-gateway/gateway-infrastructure/src/test/kotlin/team/aliens/GatewayApplicationTests.kt +++ /dev/null @@ -1,10 +0,0 @@ -package team.aliens - -import org.junit.jupiter.api.Test - -class GatewayApplicationTests { - - @Test - fun contextLoads() { - } -} diff --git a/dms-main/main-core/src/main/kotlin/team/aliens/dms/domain/auth/model/Passport.kt b/dms-main/main-core/src/main/kotlin/team/aliens/dms/domain/auth/model/Passport.kt index ffa2a8240..9fb7edf47 100644 --- a/dms-main/main-core/src/main/kotlin/team/aliens/dms/domain/auth/model/Passport.kt +++ b/dms-main/main-core/src/main/kotlin/team/aliens/dms/domain/auth/model/Passport.kt @@ -2,11 +2,6 @@ package team.aliens.dms.domain.auth.model import java.util.UUID -data class Passport( - val user: PassportUser, - val userIntegrity: String -) - data class PassportUser( val id: UUID, val schoolId: UUID, diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/config/CorsConfig.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/config/CorsConfig.kt new file mode 100644 index 000000000..906f29edd --- /dev/null +++ b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/config/CorsConfig.kt @@ -0,0 +1,29 @@ +package team.aliens.dms.global.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.cors.CorsConfiguration +import org.springframework.web.cors.CorsConfigurationSource +import org.springframework.web.cors.UrlBasedCorsConfigurationSource +import team.aliens.dms.global.security.CorsProperties + +@Configuration +class CorsConfig( + private val corsProperties: CorsProperties, +) { + + @Bean + fun corsConfigurationSource(): CorsConfigurationSource { + val configuration = CorsConfiguration().apply { + allowedOrigins = corsProperties.allowedOrigins + allowedMethods = corsProperties.allowedMethods + allowedHeaders = corsProperties.allowedHeaders + allowCredentials = corsProperties.allowCredentials + exposedHeaders = corsProperties.exposedHeaders + } + + return UrlBasedCorsConfigurationSource().apply { + registerCorsConfiguration("/**", configuration) + } + } +} diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/config/PropertiesScanConfig.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/config/PropertiesScanConfig.kt index 7a0b879b2..9fe543bbf 100644 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/config/PropertiesScanConfig.kt +++ b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/config/PropertiesScanConfig.kt @@ -2,8 +2,8 @@ package team.aliens.dms.global.config import org.springframework.boot.context.properties.ConfigurationPropertiesScan import org.springframework.context.annotation.Configuration +import team.aliens.dms.global.security.CorsProperties import team.aliens.dms.global.security.SecurityProperties -import team.aliens.dms.global.security.passport.PassportProperties import team.aliens.dms.thirdparty.AwsProperties import team.aliens.dms.thirdparty.email.SmtpProperties import team.aliens.dms.thirdparty.storage.AwsS3Properties @@ -11,10 +11,10 @@ import team.aliens.dms.thirdparty.storage.AwsS3Properties @ConfigurationPropertiesScan( basePackageClasses = [ SecurityProperties::class, + CorsProperties::class, AwsS3Properties::class, SmtpProperties::class, AwsProperties::class, - PassportProperties::class ] ) @Configuration diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/FilterConfig.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/FilterConfig.kt index 05a6dd996..67498f568 100644 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/FilterConfig.kt +++ b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/FilterConfig.kt @@ -6,17 +6,17 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.web.DefaultSecurityFilterChain import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter import org.springframework.stereotype.Component -import team.aliens.dms.global.security.passport.PassportValidator +import team.aliens.dms.global.security.token.JwtParser @Component class FilterConfig( - private val passportValidator: PassportValidator, + private val jwtParser: JwtParser, private val objectMapper: ObjectMapper, ) : SecurityConfigurer { override fun init(builder: HttpSecurity?) {} override fun configure(builder: HttpSecurity) { - builder.addFilterBefore(PassportFilter(passportValidator, objectMapper), UsernamePasswordAuthenticationFilter::class.java) - builder.addFilterBefore(ExceptionFilter(objectMapper), PassportFilter::class.java) + builder.addFilterBefore(JwtAuthenticationFilter(jwtParser), UsernamePasswordAuthenticationFilter::class.java) + builder.addFilterBefore(ExceptionFilter(objectMapper), JwtAuthenticationFilter::class.java) } } diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilter.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilter.kt new file mode 100644 index 000000000..0ffab0299 --- /dev/null +++ b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilter.kt @@ -0,0 +1,91 @@ +package team.aliens.dms.global.filter + +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.http.HttpMethod +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.util.AntPathMatcher +import org.springframework.web.filter.OncePerRequestFilter +import team.aliens.dms.domain.auth.model.Authority +import team.aliens.dms.domain.auth.model.PassportUser +import team.aliens.dms.global.security.SecurityPaths +import team.aliens.dms.global.security.exception.InvalidTokenException +import team.aliens.dms.global.security.principle.GeneralTeacherDetails +import team.aliens.dms.global.security.principle.HeadTeacherDetails +import team.aliens.dms.global.security.principle.ManagerDetails +import team.aliens.dms.global.security.principle.StudentDetails +import team.aliens.dms.global.security.token.JwtParser +import team.aliens.dms.global.security.token.JwtProperties + +class JwtAuthenticationFilter( + private val jwtParser: JwtParser +) : OncePerRequestFilter() { + + private val pathMatcher = AntPathMatcher() + + override fun shouldNotFilter(request: HttpServletRequest): Boolean { + val path = request.requestURI + val method = HttpMethod.valueOf(request.method) + return SecurityPaths.PERMIT_ALL_PATHS.any { permitPath -> + pathMatcher.match(permitPath.path, path) && + (permitPath.method == null || permitPath.method == method) + } + } + + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + val token = resolveToken(request) + val user = jwtParser.extractUserInfo(token) + + SecurityContextHolder.clearContext() + SecurityContextHolder.getContext().authentication = createAuthentication(user) + + filterChain.doFilter(request, response) + } + + private fun resolveToken(request: HttpServletRequest): String { + val authorizationHeader = request.getHeader(JwtProperties.HEADER) + ?: throw InvalidTokenException + + if (!authorizationHeader.startsWith(JwtProperties.PREFIX)) { + throw InvalidTokenException + } + + return authorizationHeader.removePrefix(JwtProperties.PREFIX) + } + + private fun createAuthentication(user: PassportUser): UsernamePasswordAuthenticationToken { + val authorities = listOf(SimpleGrantedAuthority(user.authority.name)) + + val details = when (user.authority) { + Authority.STUDENT -> StudentDetails( + userId = user.id, + schoolId = user.schoolId, + authority = user.authority + ) + Authority.MANAGER -> ManagerDetails( + userId = user.id, + schoolId = user.schoolId, + authority = user.authority + ) + Authority.GENERAL_TEACHER -> GeneralTeacherDetails( + userId = user.id, + schoolId = user.schoolId, + authority = user.authority + ) + Authority.HEAD_TEACHER -> HeadTeacherDetails( + userId = user.id, + schoolId = user.schoolId, + authority = user.authority + ) + } + + return UsernamePasswordAuthenticationToken(details, null, authorities) + } +} diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/PassportFilter.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/PassportFilter.kt deleted file mode 100644 index d8836f469..000000000 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/PassportFilter.kt +++ /dev/null @@ -1,101 +0,0 @@ -package team.aliens.dms.global.filter - -import com.fasterxml.jackson.databind.ObjectMapper -import jakarta.servlet.FilterChain -import jakarta.servlet.http.HttpServletRequest -import jakarta.servlet.http.HttpServletResponse -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.util.AntPathMatcher -import org.springframework.web.filter.OncePerRequestFilter -import team.aliens.dms.domain.auth.model.Authority -import team.aliens.dms.domain.auth.model.Passport -import team.aliens.dms.global.security.SecurityPaths -import team.aliens.dms.global.security.exception.InvalidTokenException -import team.aliens.dms.global.security.passport.PassportSecurityProperties -import team.aliens.dms.global.security.passport.PassportValidator -import team.aliens.dms.global.security.principle.GeneralTeacherDetails -import team.aliens.dms.global.security.principle.HeadTeacherDetails -import team.aliens.dms.global.security.principle.ManagerDetails -import team.aliens.dms.global.security.principle.StudentDetails - -class PassportFilter( - private val passportValidator: PassportValidator, - private val objectMapper: ObjectMapper -) : OncePerRequestFilter() { - - private val pathMatcher = AntPathMatcher() - - override fun shouldNotFilter(request: HttpServletRequest): Boolean { - val path = request.requestURI - val shouldSkip = SecurityPaths.PERMIT_ALL_PATHS.any { permitPath -> - pathMatcher.match(permitPath, path) - } - return shouldSkip - } - - override fun doFilterInternal( - request: HttpServletRequest, - response: HttpServletResponse, - filterChain: FilterChain - ) { - val passportHeader = request.getHeader(PassportSecurityProperties.PASSPORT_HEADER) - - val passport = resolvePassport(passportHeader) - - SecurityContextHolder.clearContext() - - passport.let { - val authentication = createAuthentication(it) - SecurityContextHolder.getContext().authentication = authentication - } - - filterChain.doFilter(request, response) - } - - private fun resolvePassport(passportHeader: String?): Passport { - if (passportHeader.isNullOrBlank()) { - throw InvalidTokenException - } - - return try { - val passport = objectMapper.readValue(passportHeader, Passport::class.java) - if (!passportValidator.validate(passport)) { - throw InvalidTokenException - } - passport - } catch (e: Exception) { - throw InvalidTokenException - } - } - - private fun createAuthentication(passport: Passport): UsernamePasswordAuthenticationToken { - val authorities = listOf(SimpleGrantedAuthority(passport.user.authority.name)) - - val details = when (passport.user.authority) { - Authority.STUDENT -> StudentDetails( - userId = passport.user.id, - schoolId = passport.user.schoolId, - authority = passport.user.authority - ) - Authority.MANAGER -> ManagerDetails( - userId = passport.user.id, - schoolId = passport.user.schoolId, - authority = passport.user.authority - ) - Authority.GENERAL_TEACHER -> GeneralTeacherDetails( - userId = passport.user.id, - schoolId = passport.user.schoolId, - authority = passport.user.authority - ) - Authority.HEAD_TEACHER -> HeadTeacherDetails( - userId = passport.user.id, - schoolId = passport.user.schoolId, - authority = passport.user.authority - ) - } - - return UsernamePasswordAuthenticationToken(details, null, authorities) - } -} diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/CorsProperties.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/CorsProperties.kt new file mode 100644 index 000000000..f79d1685b --- /dev/null +++ b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/CorsProperties.kt @@ -0,0 +1,12 @@ +package team.aliens.dms.global.security + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "cors") +class CorsProperties( + val allowedOrigins: List, + val allowedMethods: List, + val allowedHeaders: List, + val allowCredentials: Boolean, + val exposedHeaders: List, +) diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityConfig.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityConfig.kt index 942791833..5b5981c6c 100644 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityConfig.kt +++ b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityConfig.kt @@ -3,11 +3,11 @@ package team.aliens.dms.global.security import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.http.HttpMethod -import org.springframework.security.config.Customizer import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.http.SessionCreationPolicy import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.security.web.SecurityFilterChain +import org.springframework.web.cors.CorsConfigurationSource import team.aliens.dms.domain.auth.model.Authority.MANAGER import team.aliens.dms.domain.auth.model.Authority.STUDENT import team.aliens.dms.domain.auth.model.Authority.GENERAL_TEACHER @@ -19,7 +19,8 @@ import team.aliens.dms.global.filter.FilterConfig class SecurityConfig( private val authenticationEntryPoint: CustomAuthenticationEntryPoint, private val accessDeniedHandler: CustomAccessDeniedHandler, - private val filterConfig: FilterConfig + private val filterConfig: FilterConfig, + private val corsConfigurationSource: CorsConfigurationSource, ) { @Bean @@ -27,7 +28,7 @@ class SecurityConfig( http .csrf { it.disable() } .formLogin { it.disable() } - .cors(Customizer.withDefaults()) + .cors { it.configurationSource(corsConfigurationSource) } .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) } http diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityPaths.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityPaths.kt index 11c0c68d7..abe294cc9 100644 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityPaths.kt +++ b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityPaths.kt @@ -1,42 +1,50 @@ package team.aliens.dms.global.security +import org.springframework.http.HttpMethod + object SecurityPaths { + data class PermitAllPath( + val method: HttpMethod?, + val path: String, + ) + val PERMIT_ALL_PATHS = listOf( // healthcheck - "/", + PermitAllPath(null, "/"), // auth - "/auth/account-id", - "/auth/email", - "/auth/code", - "/auth/tokens", - "/auth/reissue", - "/auth/passport", + PermitAllPath(HttpMethod.GET, "/auth/account-id"), + PermitAllPath(HttpMethod.GET, "/auth/email"), + PermitAllPath(HttpMethod.GET, "/auth/code"), + PermitAllPath(HttpMethod.POST, "/auth/code"), + PermitAllPath(HttpMethod.POST, "/auth/tokens"), + PermitAllPath(HttpMethod.PUT, "/auth/reissue"), + PermitAllPath(HttpMethod.POST, "/auth/passport"), // students - "/students/email/duplication", - "/students/account-id/duplication", - "/students/account-id/{school-id}", - "/students/name", - "/students/signup", - "/students/password/initialization", + PermitAllPath(HttpMethod.GET, "/students/email/duplication"), + PermitAllPath(HttpMethod.GET, "/students/account-id/duplication"), + PermitAllPath(HttpMethod.GET, "/students/account-id/{school-id}"), + PermitAllPath(HttpMethod.GET, "/students/name"), + PermitAllPath(HttpMethod.POST, "/students/signup"), + PermitAllPath(HttpMethod.PATCH, "/students/password/initialization"), // managers - "/managers/account-id/{school-id}", - "/managers/password/initialization", + PermitAllPath(HttpMethod.GET, "/managers/account-id/{school-id}"), + PermitAllPath(HttpMethod.PATCH, "/managers/password/initialization"), // schools - "/schools", - "/schools/question/{school-id}", - "/schools/answer/{school-id}", - "/schools/code", + PermitAllPath(HttpMethod.GET, "/schools"), + PermitAllPath(HttpMethod.GET, "/schools/question/{school-id}"), + PermitAllPath(HttpMethod.GET, "/schools/answer/{school-id}"), + PermitAllPath(HttpMethod.GET, "/schools/code"), // files - "/files", - "/files/url", + PermitAllPath(HttpMethod.POST, "/files"), + PermitAllPath(HttpMethod.GET, "/files/url"), - // templates - "/templates", + // templates (모든 메서드 permitAll) + PermitAllPath(null, "/templates"), ) } diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportIntegrityGenerator.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportIntegrityGenerator.kt deleted file mode 100644 index 4fa9d8032..000000000 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportIntegrityGenerator.kt +++ /dev/null @@ -1,21 +0,0 @@ -package team.aliens.dms.global.security.passport - -import org.springframework.stereotype.Component -import team.aliens.dms.domain.auth.model.PassportUser -import java.util.Base64 -import javax.crypto.Mac -import javax.crypto.spec.SecretKeySpec - -@Component -class PassportIntegrityGenerator( - private val passportProperties: PassportProperties -) { - fun generate(passportUser: PassportUser): String { - val data = "${passportUser.id}|${passportUser.schoolId}|${passportUser.authority}" - val hmacKey = SecretKeySpec(passportProperties.secretKey.encoded, PassportSecurityProperties.HMAC_ALGORITHM) - val mac = Mac.getInstance(PassportSecurityProperties.HMAC_ALGORITHM) - mac.init(hmacKey) - val hmacBytes = mac.doFinal(data.toByteArray()) - return Base64.getEncoder().encodeToString(hmacBytes) - } -} diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportProperties.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportProperties.kt deleted file mode 100644 index 05929481e..000000000 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportProperties.kt +++ /dev/null @@ -1,14 +0,0 @@ -package team.aliens.dms.global.security.passport - -import io.jsonwebtoken.security.Keys -import org.springframework.boot.context.properties.ConfigurationProperties -import javax.crypto.SecretKey - -@ConfigurationProperties(prefix = "passport") -class PassportProperties( - secretKey: String -) { - val secretKey: SecretKey = Keys.hmacShaKeyFor( - secretKey.toByteArray(Charsets.UTF_8) - ) -} diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportSecurityProperties.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportSecurityProperties.kt deleted file mode 100644 index 809a33db5..000000000 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportSecurityProperties.kt +++ /dev/null @@ -1,6 +0,0 @@ -package team.aliens.dms.global.security.passport - -object PassportSecurityProperties { - const val PASSPORT_HEADER = "X-PASSPORT" - const val HMAC_ALGORITHM = "HmacSHA256" -} diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportValidator.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportValidator.kt deleted file mode 100644 index 824e6282f..000000000 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/passport/PassportValidator.kt +++ /dev/null @@ -1,18 +0,0 @@ -package team.aliens.dms.global.security.passport - -import org.springframework.stereotype.Component -import team.aliens.dms.domain.auth.model.Passport -import java.security.MessageDigest - -@Component -class PassportValidator( - private val passportIntegrityGenerator: PassportIntegrityGenerator -) { - fun validate(passport: Passport): Boolean { - val expectedHmac = passportIntegrityGenerator.generate(passport.user) - return MessageDigest.isEqual( - expectedHmac.toByteArray(), - passport.userIntegrity.toByteArray() - ) - } -} diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/token/JwtParser.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/token/JwtParser.kt new file mode 100644 index 000000000..ff463a51f --- /dev/null +++ b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/token/JwtParser.kt @@ -0,0 +1,71 @@ +package team.aliens.dms.global.security.token + +import io.jsonwebtoken.Claims +import io.jsonwebtoken.ExpiredJwtException +import io.jsonwebtoken.Header +import io.jsonwebtoken.InvalidClaimException +import io.jsonwebtoken.Jws +import io.jsonwebtoken.JwtException +import io.jsonwebtoken.Jwts +import org.springframework.stereotype.Component +import team.aliens.dms.domain.auth.model.Authority +import team.aliens.dms.domain.auth.model.PassportUser +import team.aliens.dms.global.error.InternalServerError +import team.aliens.dms.global.security.SecurityProperties +import team.aliens.dms.global.security.exception.ExpiredTokenException +import team.aliens.dms.global.security.exception.InvalidTokenException +import java.util.UUID + +@Component +class JwtParser( + private val securityProperties: SecurityProperties +) { + + fun extractUserInfo(token: String): PassportUser { + val claims = getClaims(token) + + if (claims.header[Header.TYPE] != JwtProperties.ACCESS) { + throw InvalidTokenException + } + + return parseUserInfo(claims.body) + } + + private fun getClaims(token: String): Jws { + return try { + Jwts.parserBuilder() + .setSigningKey(securityProperties.secretKey) + .build() + .parseClaimsJws(token) + } catch (e: ExpiredJwtException) { + throw ExpiredTokenException + } catch (e: InvalidClaimException) { + throw InvalidTokenException + } catch (e: JwtException) { + throw InvalidTokenException + } catch (e: Exception) { + throw InternalServerError + } + } + + private fun parseUserInfo(claims: Claims): PassportUser { + val authorityValue = claims.get(JwtProperties.AUTHORITY, String::class.java) + ?: throw InvalidTokenException + + val authority = try { + Authority.valueOf(authorityValue) + } catch (e: IllegalArgumentException) { + throw InvalidTokenException + } + + return try { + PassportUser( + id = UUID.fromString(claims.id), + schoolId = UUID.fromString(claims.get(JwtProperties.SCHOOL_ID, String::class.java)), + authority = authority + ) + } catch (e: Exception) { + throw InvalidTokenException + } + } +} diff --git a/dms-main/main-infrastructure/src/main/resources/application.yml b/dms-main/main-infrastructure/src/main/resources/application.yml index 322de589c..a9a822ab1 100644 --- a/dms-main/main-infrastructure/src/main/resources/application.yml +++ b/dms-main/main-infrastructure/src/main/resources/application.yml @@ -99,6 +99,29 @@ secret: access-exp: ${ACCESS_EXP:3600} refresh-exp: ${REFRESH_EXP:1205600} +cors: + allowed-origins: + - "http://localhost:3000" + - "http://localhost:3001" + - "http://localhost:3002" + - "https://admin-dms.dsmhs.kr" + - "https://admin-dev-dms.dsmhs.kr" + - "https://webview-dms.dsmhs.kr" + - "https://head-teacher-dms.dsmhs.kr" + - "https://general-teacher-dms.dsmhs.kr" + - "https://student-dms.dsmhs.kr" + allowed-methods: + - GET + - POST + - PUT + - DELETE + - PATCH + - OPTIONS + allowed-headers: "*" + allow-credentials: true + exposed-headers: + - "Content-Disposition" + open-feign: neis-key: ${NEIS_KEY:asdf} @@ -134,6 +157,3 @@ logging: amazonaws: util: EC2MetadataUtils: error - -passport: - secret-key: ${PASSPORT_SECRET_KEY:qwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiop} diff --git a/dms-main/main-infrastructure/src/test/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilterTest.kt b/dms-main/main-infrastructure/src/test/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilterTest.kt new file mode 100644 index 000000000..f78001bb6 --- /dev/null +++ b/dms-main/main-infrastructure/src/test/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilterTest.kt @@ -0,0 +1,138 @@ +package team.aliens.dms.global.filter + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.IsolationMode +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.data.forAll +import io.kotest.data.row +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import jakarta.servlet.DispatcherType +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.security.core.context.SecurityContextHolder +import team.aliens.dms.domain.auth.model.Authority +import team.aliens.dms.domain.auth.model.PassportUser +import team.aliens.dms.global.security.exception.InvalidTokenException +import team.aliens.dms.global.security.principle.CustomDetails +import team.aliens.dms.global.security.principle.GeneralTeacherDetails +import team.aliens.dms.global.security.principle.HeadTeacherDetails +import team.aliens.dms.global.security.principle.ManagerDetails +import team.aliens.dms.global.security.principle.StudentDetails +import team.aliens.dms.global.security.token.JwtParser +import team.aliens.dms.global.security.token.JwtProperties +import java.util.UUID +import kotlin.reflect.KClass + +private fun mockRequest(uri: String, authorizationHeader: String?, method: String = "GET"): HttpServletRequest { + val request = mockk(relaxed = true) + every { request.requestURI } returns uri + every { request.method } returns method + every { request.dispatcherType } returns DispatcherType.REQUEST + every { request.getHeader(JwtProperties.HEADER) } returns authorizationHeader + every { request.getAttribute(any()) } returns null + return request +} + +class JwtAuthenticationFilterTest : DescribeSpec({ + + isolationMode = IsolationMode.InstancePerLeaf + + afterTest { SecurityContextHolder.clearContext() } + + val jwtParser = mockk() + val filter = JwtAuthenticationFilter(jwtParser) + val response = mockk(relaxed = true) + val filterChain = mockk(relaxed = true) + + describe("doFilter") { + context("permitAll 경로면") { + val request = mockRequest(uri = "/auth/tokens", method = "POST", authorizationHeader = null) + + it("JWT 검증 없이 다음 필터로 넘긴다") { + filter.doFilter(request, response, filterChain) + + verify(exactly = 0) { jwtParser.extractUserInfo(any()) } + verify(exactly = 1) { filterChain.doFilter(request, response) } + } + } + + context("permitAll 경로여도 등록된 메서드가 아니면 (/schools/code)") { + it("GET은 JWT 검증 없이 다음 필터로 넘긴다") { + val request = mockRequest(uri = "/schools/code", method = "GET", authorizationHeader = null) + + filter.doFilter(request, response, filterChain) + + verify(exactly = 0) { jwtParser.extractUserInfo(any()) } + verify(exactly = 1) { filterChain.doFilter(request, response) } + } + + it("PATCH는 JWT 검증을 한다") { + val request = mockRequest(uri = "/schools/code", method = "PATCH", authorizationHeader = null) + + shouldThrow { + filter.doFilter(request, response, filterChain) + } + verify(exactly = 0) { filterChain.doFilter(any(), any()) } + } + } + + context("인증이 필요한 경로인데 Authorization 헤더가 없으면") { + val request = mockRequest(uri = "/students", authorizationHeader = null) + + it("InvalidTokenException을 던지고 다음 필터로 넘어가지 않는다") { + shouldThrow { + filter.doFilter(request, response, filterChain) + } + verify(exactly = 0) { filterChain.doFilter(any(), any()) } + } + } + + context("Bearer 프리픽스가 없으면") { + val request = mockRequest(uri = "/students", authorizationHeader = "raw-token-without-prefix") + + it("InvalidTokenException을 던진다") { + shouldThrow { + filter.doFilter(request, response, filterChain) + } + } + } + + context("정상 토큰이면") { + it("권한에 맞는 Details를 SecurityContext에 저장하고 다음 필터로 넘긴다") { + forAll( + row(Authority.STUDENT, StudentDetails::class), + row(Authority.MANAGER, ManagerDetails::class), + row(Authority.GENERAL_TEACHER, GeneralTeacherDetails::class), + row(Authority.HEAD_TEACHER, HeadTeacherDetails::class), + ) { authority: Authority, expectedDetailsType: KClass -> + + val userId = UUID.randomUUID() + val schoolId = UUID.randomUUID() + val token = "token-$authority" + val request = mockRequest(uri = "/students", authorizationHeader = "Bearer $token") + every { jwtParser.extractUserInfo(token) } returns PassportUser( + id = userId, + schoolId = schoolId, + authority = authority + ) + + filter.doFilter(request, response, filterChain) + + val details = SecurityContextHolder.getContext().authentication.principal as CustomDetails + details::class shouldBe expectedDetailsType + details.userId shouldBe userId + details.schoolId shouldBe schoolId + details.authority shouldBe authority + + verify(exactly = 1) { filterChain.doFilter(request, response) } + + SecurityContextHolder.clearContext() + } + } + } + } +}) diff --git a/dms-main/main-infrastructure/src/test/kotlin/team/aliens/dms/global/security/token/JwtParserTest.kt b/dms-main/main-infrastructure/src/test/kotlin/team/aliens/dms/global/security/token/JwtParserTest.kt new file mode 100644 index 000000000..791cc236f --- /dev/null +++ b/dms-main/main-infrastructure/src/test/kotlin/team/aliens/dms/global/security/token/JwtParserTest.kt @@ -0,0 +1,123 @@ +package team.aliens.dms.global.security.token + +import io.jsonwebtoken.Header +import io.jsonwebtoken.Jwts +import io.jsonwebtoken.SignatureAlgorithm +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe +import team.aliens.dms.domain.auth.model.Authority +import team.aliens.dms.global.security.SecurityProperties +import team.aliens.dms.global.security.exception.ExpiredTokenException +import team.aliens.dms.global.security.exception.InvalidTokenException +import java.util.Date +import java.util.UUID + +private fun buildToken( + securityProperties: SecurityProperties, + type: String = JwtProperties.ACCESS, + userId: UUID = UUID.randomUUID(), + schoolId: UUID = UUID.randomUUID(), + authority: String? = Authority.STUDENT.name, + expiration: Date = Date(System.currentTimeMillis() + 3_600_000) +): String { + val builder = Jwts.builder() + .signWith(securityProperties.secretKey, SignatureAlgorithm.HS512) + .setHeaderParam(Header.TYPE, type) + .setId(userId.toString()) + .claim(JwtProperties.SCHOOL_ID, schoolId.toString()) + .setIssuedAt(Date()) + .setExpiration(expiration) + + if (authority != null) { + builder.claim(JwtProperties.AUTHORITY, authority) + } + + return builder.compact() +} + +class JwtParserTest : DescribeSpec({ + + val securityProperties = SecurityProperties( + secretKey = "test-jwt-secret-key-".repeat(4), + accessExp = 3600, + refreshExp = 1_205_600 + ) + val jwtParser = JwtParser(securityProperties) + + describe("extractUserInfo") { + context("유효한 access 토큰이면") { + val userId = UUID.randomUUID() + val schoolId = UUID.randomUUID() + val token = buildToken( + securityProperties, + userId = userId, + schoolId = schoolId, + authority = Authority.MANAGER.name + ) + + it("토큰의 클레임으로 PassportUser를 만든다") { + val result = jwtParser.extractUserInfo(token) + + result.id shouldBe userId + result.schoolId shouldBe schoolId + result.authority shouldBe Authority.MANAGER + } + } + + context("만료된 토큰이면") { + val token = buildToken(securityProperties, expiration = Date(System.currentTimeMillis() - 1_000)) + + it("ExpiredTokenException을 던진다") { + shouldThrow { + jwtParser.extractUserInfo(token) + } + } + } + + context("다른 시크릿으로 서명된(위조된) 토큰이면") { + val forgedProperties = SecurityProperties( + secretKey = "forged-jwt-secret-key-".repeat(4), + accessExp = 3600, + refreshExp = 1_205_600 + ) + val token = buildToken(forgedProperties) + + it("InvalidTokenException을 던진다") { + shouldThrow { + jwtParser.extractUserInfo(token) + } + } + } + + context("refresh 타입 토큰이면") { + val token = buildToken(securityProperties, type = JwtProperties.REFRESH) + + it("InvalidTokenException을 던진다") { + shouldThrow { + jwtParser.extractUserInfo(token) + } + } + } + + context("authority 클레임이 유효하지 않은 값이면") { + val token = buildToken(securityProperties, authority = "NOT_A_REAL_AUTHORITY") + + it("InvalidTokenException을 던진다") { + shouldThrow { + jwtParser.extractUserInfo(token) + } + } + } + + context("authority 클레임이 아예 없으면") { + val token = buildToken(securityProperties, authority = null) + + it("InvalidTokenException을 던진다") { + shouldThrow { + jwtParser.extractUserInfo(token) + } + } + } + } +}) diff --git a/settings.gradle.kts b/settings.gradle.kts index 6f4a95e6b..58017fe95 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,11 +7,6 @@ include(":dms-main:main-persistence") include(":dms-main:main-infrastructure") include(":dms-main:main-presentation") -// gateway -include(":dms-gateway") -include(":dms-gateway:gateway-core") -include(":dms-gateway:gateway-infrastructure") - // contracts include(":contracts") include(":contracts:enum-contracts")