Skip to content

Commit

Permalink
fix yarn support together with swagger
Browse files Browse the repository at this point in the history
Signed-off-by: Klaus Lehner <[email protected]>
  • Loading branch information
klu2 committed Mar 6, 2023
1 parent f27bfe7 commit a9df7fc
Show file tree
Hide file tree
Showing 95 changed files with 14,681 additions and 5 deletions.
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,11 @@ dist
lint.js

.angular
coverage
coverage

samples/kotlin-springboot-angular-yarn/skeleton-ui/.yarn/*
!samples/kotlin-springboot-angular-yarn/skeleton-ui/.yarn/releases
!samples/kotlin-springboot-angular-yarn/skeleton-ui/.yarn/patches
!samples/kotlin-springboot-angular-yarn/skeleton-ui/.yarn/plugins
!samples/kotlin-springboot-angular-yarn/skeleton-ui/.yarn/sdks
!samples/kotlin-springboot-angular-yarn/skeleton-ui/.yarn/versions
24 changes: 24 additions & 0 deletions samples/kotlin-springboot-angular-yarn/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
plugins {
id "io.cloudflight.autoconfigure-gradle"
}

allprojects {
description "Cloudflight Angular Kotlin Skeleton"
group "io.cloudflight.skeleton.angular"
version "1.0.0"

repositories {
mavenCentral()
}
}

subprojects {
if (!it.name.endsWith("-ui")) {
dependencies {
implementation platform(libs.cloudflight.platform.spring.bom)
annotationProcessor platform(libs.cloudflight.platform.spring.bom)
testImplementation platform(libs.cloudflight.platform.spring.test.bom)
kapt platform(libs.cloudflight.platform.spring.bom)
}
}
}
2 changes: 2 additions & 0 deletions samples/kotlin-springboot-angular-yarn/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
kapt.include.compile.classpath=false
kotlin.code.style=official
12 changes: 12 additions & 0 deletions samples/kotlin-springboot-angular-yarn/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[versions]
clf-platform-spring = "0.9.3"

[libraries]

cloudflight-platform-spring-bom = { module = "io.cloudflight.platform.spring:platform-spring-bom", version.ref = "clf-platform-spring" }
cloudflight-platform-spring-test-bom = { module = "io.cloudflight.platform.spring:platform-spring-test-bom", version.ref = "clf-platform-spring" }

[bundles]

[plugins]

5 changes: 5 additions & 0 deletions samples/kotlin-springboot-angular-yarn/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
rootProject.name = 'kotlin-springboot-angular-yarn'

include 'skeleton-api'
include 'skeleton-server'
include 'skeleton-ui'
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
plugins {
id "io.cloudflight.autoconfigure.swagger-api-configure"
}

dependencies {
implementation 'io.swagger:swagger-annotations'

implementation 'org.springframework:spring-web'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.cloudflight.skeleton.angular.api

import io.cloudflight.skeleton.angular.api.dto.OutputGreeting
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam

@Api("HelloWorld")
interface HelloWorldApi {

@ApiOperation("Returns a greeting from the server")
@GetMapping("$CONTEXT_PATH/hello")
fun getHello(): OutputGreeting

@ApiOperation("Returns a greeting from the admin")
@GetMapping("$CONTEXT_PATH/hello-admin")
fun getHelloAdmin(@RequestParam() name: String): OutputGreeting

companion object {
private const val CONTEXT_PATH = "/api"
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.cloudflight.skeleton.angular.api

import io.cloudflight.skeleton.angular.api.dto.CurrentUserDto
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import org.springframework.web.bind.annotation.GetMapping

@Api("User")
interface UserApi {

@ApiOperation("Returns the current user")
@GetMapping("$CONTEXT_PATH/user")
fun getCurrentUser(): CurrentUserDto

companion object {
private const val CONTEXT_PATH = "/api"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package io.cloudflight.skeleton.angular.api.dto

class CurrentUserDto(val userName: String, val name: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package io.cloudflight.skeleton.angular.api.dto

class OutputGreeting(val casual: String, val formal: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
dependencies {
implementation project(':skeleton-api')
implementation project(':skeleton-ui')

implementation 'io.cloudflight.platform.spring:platform-spring-i18n'
implementation 'io.cloudflight.platform.spring:platform-spring-logging'
implementation 'io.cloudflight.platform.spring:platform-spring-json'
implementation 'io.cloudflight.platform.spring:platform-spring-server-config'
testImplementation 'io.cloudflight.platform.spring:platform-spring-test'

implementation 'org.springframework.boot:spring-boot-actuator'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.cloudflight.skeleton.angular

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.scheduling.annotation.EnableScheduling

@SpringBootApplication
@EnableScheduling
class Application

fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.cloudflight.skeleton.angular.config

import org.springframework.boot.web.server.ErrorPage
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpStatus
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

/**
* this is needed to support HTML5 routing on frontend
*/
@Configuration
class WebConfiguration : WebMvcConfigurer {
override fun addViewControllers(registry: ViewControllerRegistry) {
registry.addViewController("/404").setViewName("forward:/index.html")
}

@Bean
fun containerCustomizer(): WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
return WebServerFactoryCustomizer { container ->
container.addErrorPages(ErrorPage(HttpStatus.NOT_FOUND, "/404"))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.cloudflight.skeleton.angular.controller

import io.cloudflight.skeleton.angular.security.service.SecurityService
import io.cloudflight.skeleton.angular.api.HelloWorldApi
import io.cloudflight.skeleton.angular.api.dto.OutputGreeting
import org.springframework.web.bind.annotation.RestController

@RestController
class HelloWorldController(
private val securityService: SecurityService
) : HelloWorldApi {

override fun getHello(): OutputGreeting {
return OutputGreeting("Hello ;)", "Pleasure to meet you!")
}

override fun getHelloAdmin(name: String): OutputGreeting {
securityService.assertAdminAccess()
return OutputGreeting("Hello $name ;)", "Pleasure to meet you $name!")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.cloudflight.skeleton.angular.controller

import io.cloudflight.skeleton.angular.api.UserApi
import io.cloudflight.skeleton.angular.api.dto.CurrentUserDto
import io.cloudflight.skeleton.angular.security.service.SecurityService
import org.springframework.web.bind.annotation.RestController

@RestController
class UserController(
private val securityService: SecurityService
) : UserApi {

override fun getCurrentUser(): CurrentUserDto {
return securityService.currentUser.let {
CurrentUserDto(userName = it.userName, name = "${it.firstName} ${it.lastName}")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.cloudflight.skeleton.angular.dto

class User(
val id: Long,
val username: String,
val name: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@file:JvmName("UserRoles")

package io.cloudflight.skeleton.angular.security

const val ROLE_ADMIN = "ROLE_ADMIN"
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.cloudflight.skeleton.angular.security.config

import org.springframework.boot.autoconfigure.security.servlet.PathRequest
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.builders.WebSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.web.csrf.CookieCsrfTokenRepository
import org.springframework.security.web.header.writers.HstsHeaderWriter
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter
import org.springframework.security.web.header.writers.XContentTypeOptionsHeaderWriter
import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
class WebSecurityConfiguration : WebSecurityConfigurerAdapter() {

override fun configure(http: HttpSecurity) {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.authorizeRequests()
.anyRequest().permitAll()
.and()
.httpBasic()
.and()
.logout().permitAll()

http.headers()
.addHeaderWriter(XContentTypeOptionsHeaderWriter())
.addHeaderWriter(XFrameOptionsHeaderWriter())
.addHeaderWriter(ReferrerPolicyHeaderWriter())
.addHeaderWriter(XXssProtectionHeaderWriter())
.addHeaderWriter(HstsHeaderWriter())
}

override fun configure(webSecurity: WebSecurity) {
webSecurity.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations())
}

@Bean
fun passwordEncoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.cloudflight.skeleton.angular.security.model

import io.cloudflight.skeleton.angular.security.ROLE_ADMIN
import org.springframework.security.core.GrantedAuthority

interface CurrentUser {

val isAdmin: Boolean
get() = hasRole(ROLE_ADMIN)

val userName: String

val firstName: String
val lastName: String

fun getAuthorities(): Collection<GrantedAuthority>

fun hasRole(role: String): Boolean {
return getAuthorities().stream().anyMatch { r -> r.authority.equals(role, ignoreCase = true) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.cloudflight.skeleton.angular.security.model

import org.springframework.security.core.GrantedAuthority

internal class CurrentUserImpl(
override val userName: String,
password: String,
authorities: Collection<GrantedAuthority> = emptyList(),
override val firstName: String,
override val lastName: String,
) : org.springframework.security.core.userdetails.User(userName, password, authorities), CurrentUser
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.cloudflight.skeleton.angular.security.service

import io.cloudflight.skeleton.angular.security.model.CurrentUser

interface SecurityService {
val currentUser: CurrentUser

fun assertAdminAccess()

fun getUserName(): String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.cloudflight.skeleton.angular.security.service.impl

import io.cloudflight.skeleton.angular.security.model.CurrentUser
import io.cloudflight.skeleton.angular.security.service.SecurityService
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Service


@Service
class SecurityServiceImpl : SecurityService {
override val currentUser: CurrentUser
get() {
val token = SecurityContextHolder.getContext().authentication
return if (token is CurrentUser) {
token
} else if (token is UsernamePasswordAuthenticationToken) {
token.principal as CurrentUser
} else {
throw AccessDeniedException("User not logged in.")
}
}
val currentUserAvailable: Boolean
get() = SecurityContextHolder.getContext().authentication != null

override fun assertAdminAccess() {
if (!currentUser.isAdmin) throw AccessDeniedException("User does not have admin access")
}

override fun getUserName(): String {
return if (currentUserAvailable) {currentUser.userName} else { "User not logged in" }
}
}
Loading

0 comments on commit a9df7fc

Please sign in to comment.