diff --git a/spring-boot/spring-boot-codefirst/build.gradle b/spring-boot/spring-boot-codefirst/build.gradle index aa5a974..eb1ab2b 100644 --- a/spring-boot/spring-boot-codefirst/build.gradle +++ b/spring-boot/spring-boot-codefirst/build.gradle @@ -1,12 +1,12 @@ plugins { - id 'org.springframework.boot' version '2.2.6.RELEASE' + id 'org.springframework.boot' version '2.3.0.RELEASE' id 'io.spring.dependency-management' version '1.0.9.RELEASE' id 'java' } group = 'io.reflectoring' version = '0.0.1-SNAPSHOT' -sourceCompatibility = '14' +sourceCompatibility = '1.8' configurations { compileOnly { @@ -19,7 +19,9 @@ repositories { } dependencies { + implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springdoc:springdoc-openapi-ui:1.3.3' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' diff --git a/spring-boot/spring-boot-codefirst/src/main/java/io/reflectoring/codefirst/CodeFirstApplication.java b/spring-boot/spring-boot-codefirst/src/main/java/io/reflectoring/codefirst/CodeFirstApplication.java index d0d07d5..6164a11 100644 --- a/spring-boot/spring-boot-codefirst/src/main/java/io/reflectoring/codefirst/CodeFirstApplication.java +++ b/spring-boot/spring-boot-codefirst/src/main/java/io/reflectoring/codefirst/CodeFirstApplication.java @@ -1,13 +1,178 @@ package io.reflectoring.codefirst; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Contact; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.info.License; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpStatus; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +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.web.bind.annotation.*; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +@RequestMapping("/api/todos") +@Tag(name = "Todo API", description = "euismod in pellentesque massa placerat duis ultricies lacus sed turpis") +@SecurityRequirement(name = "api") +interface TodoApi { + + @GetMapping + @ResponseStatus(code = HttpStatus.OK) + List findAll(); + + @GetMapping("/{id}") + @ResponseStatus(code = HttpStatus.OK) + Todo findById(@PathVariable String id); + + @PostMapping + @ResponseStatus(code = HttpStatus.CREATED) + Todo save(@RequestBody Todo todo); + + @PutMapping("/{id}") + @ResponseStatus(code = HttpStatus.OK) + Todo update(@PathVariable String id, @RequestBody Todo todo); + + @DeleteMapping("/{id}") + @ResponseStatus(code = HttpStatus.NO_CONTENT) + void delete(@PathVariable String id); +} + +@RequestMapping("/unsecured") +@Tag(name = "Unsecured API", description = "aliquet nec ullamcorper sit amet risus nullam eget felis eget") +interface UnsecuredApi { + + @GetMapping + @ResponseStatus(code = HttpStatus.OK) + String unsecured(); +} @SpringBootApplication public class CodeFirstApplication { - public static void main(String[] args) { SpringApplication.run(CodeFirstApplication.class, args); } - } + +@OpenAPIDefinition( + info = @Info( + title = "Code-First Approach (reflectoring.io)", + description = "" + + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur et rhoncus quam. Aenean quis augue ac eros pulvinar malesuada. " + + "In sagittis elit egestas tincidunt iaculis. " + + "Donec eu lacus vitae nulla varius consectetur a vel quam. Aliquam erat volutpat. Duis eget ullamcorper tellus", + contact = @Contact(name = "Reflectoring", url = "https://reflectoring.io", email = "petros.stergioulas94@gmail.com"), + license = @License(name = "MIT Licence", url = "https://github.com/thombergs/code-examples/blob/master/LICENSE")), + servers = @Server(url = "http://localhost:8080") +) +@SecurityScheme(scheme = "basic", type = SecuritySchemeType.HTTP, name = "api", in = SecuritySchemeIn.HEADER) +class OpenAPIConfiguration { +} + +@EnableWebSecurity +class SecurityConfiguration extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests() + .antMatchers("/unsecured", "/swagger-ui/**", "/reflectoring-openapi/**").permitAll() + .anyRequest().authenticated() + .and() + .httpBasic(); + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication() + .withUser("admin") + .password(passwordEncoder().encode("password")) + .authorities("ADMIN"); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} + +@NoArgsConstructor +@AllArgsConstructor +@Data +class Todo { + private String id; + private String text; +} + +@RestController +class TodoController implements TodoApi { + + private List todos; + + @Override + public List findAll() { + return todos; + } + + @Override + public Todo findById(String id) { + return todos.stream().filter(todo -> todo.getId().equals(id)).findFirst().orElseGet(null); + } + + @Override + public Todo save(Todo todo) { + todos.add(todo); + return todo; + } + + @Override + public Todo update(String id, Todo todo) { + for (Todo t : todos) { + if (t.getId().equals(id)) { + t.setText(todo.getText()); + } + } + + return todo; + } + + @Override + public void delete(String id) { + todos.removeIf(t -> t.getId().equals(id)); + } + + @PostConstruct + public void onInit() { + todos = Stream.of("Groceries", "Lisa's birthday") + .map(todo -> new Todo(UUID.randomUUID().toString(), todo)) + .collect(Collectors.toList()); + } +} + +@RestController +class Unsecured implements UnsecuredApi { + + @Override + public String unsecured() { + return "unsecured"; + } +} \ No newline at end of file diff --git a/spring-boot/spring-boot-codefirst/src/main/resources/application.properties b/spring-boot/spring-boot-codefirst/src/main/resources/application.properties deleted file mode 100644 index 8b13789..0000000 --- a/spring-boot/spring-boot-codefirst/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/spring-boot/spring-boot-codefirst/src/main/resources/application.yml b/spring-boot/spring-boot-codefirst/src/main/resources/application.yml new file mode 100644 index 0000000..66e4bba --- /dev/null +++ b/spring-boot/spring-boot-codefirst/src/main/resources/application.yml @@ -0,0 +1,9 @@ +springdoc: + api-docs: + path: /reflectoring-openapi + show-actuator: true +management: + endpoints: + web: + exposure: + include: "*"