From 8834071b09c32e02d0f93c869355456bc829ab7a Mon Sep 17 00:00:00 2001 From: parap Date: Mon, 15 May 2023 19:30:26 +0400 Subject: [PATCH] lab6 mvc --- build.gradle | 3 + data.mv.db | Bin 155648 -> 167936 bytes .../java/ru/ip/labs/labs/LabsApplication.java | 3 + .../PasswordEncoderConfiguration.java | 14 ++++ .../configuration/SecurityConfiguration.java | 67 ++++++++++++++++ .../{ => configuration}/WebConfiguration.java | 3 +- .../films/controller/ActorController.java | 2 +- .../labs/films/controller/FilmController.java | 5 +- .../films/controller/GenreController.java | 6 +- .../films/controller/UserMvcController.java | 42 ++++++++++ .../controller/UserSignupMvcController.java | 51 ++++++++++++ .../ru/ip/labs/labs/films/dto/UserDto.java | 28 +++++++ .../ru/ip/labs/labs/films/models/User.java | 73 ++++++++++++++++++ .../ip/labs/labs/films/models/UserRole.java | 20 +++++ .../labs/labs/films/models/UserSignupDto.java | 40 ++++++++++ .../labs/films/repository/UserRepository.java | 8 ++ .../labs/labs/films/service/UserService.java | 67 ++++++++++++++++ .../films/util/error/AdviceController.java | 37 +++++++++ .../util/validation/ValidationException.java | 13 ++++ .../films/util/validation/ValidatorUtil.java | 27 +++++++ src/main/resources/templates/default.html | 12 ++- src/main/resources/templates/login.html | 30 +++++++ src/main/resources/templates/signup.html | 28 +++++++ src/main/resources/templates/users.html | 37 +++++++++ 24 files changed, 604 insertions(+), 12 deletions(-) create mode 100644 src/main/java/ru/ip/labs/labs/configuration/PasswordEncoderConfiguration.java create mode 100644 src/main/java/ru/ip/labs/labs/configuration/SecurityConfiguration.java rename src/main/java/ru/ip/labs/labs/{ => configuration}/WebConfiguration.java (92%) create mode 100644 src/main/java/ru/ip/labs/labs/films/controller/UserMvcController.java create mode 100644 src/main/java/ru/ip/labs/labs/films/controller/UserSignupMvcController.java create mode 100644 src/main/java/ru/ip/labs/labs/films/dto/UserDto.java create mode 100644 src/main/java/ru/ip/labs/labs/films/models/User.java create mode 100644 src/main/java/ru/ip/labs/labs/films/models/UserRole.java create mode 100644 src/main/java/ru/ip/labs/labs/films/models/UserSignupDto.java create mode 100644 src/main/java/ru/ip/labs/labs/films/repository/UserRepository.java create mode 100644 src/main/java/ru/ip/labs/labs/films/service/UserService.java create mode 100644 src/main/java/ru/ip/labs/labs/films/util/error/AdviceController.java create mode 100644 src/main/java/ru/ip/labs/labs/films/util/validation/ValidationException.java create mode 100644 src/main/java/ru/ip/labs/labs/films/util/validation/ValidatorUtil.java create mode 100644 src/main/resources/templates/login.html create mode 100644 src/main/resources/templates/signup.html create mode 100644 src/main/resources/templates/users.html diff --git a/build.gradle b/build.gradle index e1ad653..8fd4e4b 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,9 @@ dependencies { implementation 'org.hibernate.validator:hibernate-validator' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' + implementation 'org.springdoc:springdoc-openapi-ui:1.6.5' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/data.mv.db b/data.mv.db index 0b87f4ade90b6ed9852b3611dbfe975e2d2d378c..2c4a8476735edb40bcae6df6873821f97cf6c95d 100644 GIT binary patch delta 8744 zcmeHMYiu0V6`nJ@>wP#*{0y;^*t?6J*l4k5X6Lc94Ncb1#BtV-#0ljgn4L#r;@C;- zgb-2eMx+7MR)mW(v;wlC3ectqtfgH*f~269_D4mLP*j2_36+{cMFc_usY*-lJa%^+ z+eze7x;rki3a^R`6_}EaE<#@|Dz2I5?`FQL^R3y&b9MxLWLO=+ z@$8Ux#u-h(-)mSC{tdC)^%sPznP>=@XnKM4@_ z^z?SEOQLjBM*p;9zoH|6m8+vc2PG=kn|C z=-onfC6WxqiB$JMD@6_VbwxKPn9k%K=FNAob3|l5fb6&Ya4YF`FU67l7(b+Gp-l{4 zV)d{cQe)U$DBLu^*a(8y99RNhcz~F!Bs`Oq>m+5z&`8OudPdq(PgC_uUo1L0G`_aC zt&5Uc`$sxE$F{M1TDoIBoqH1k)7Ra)p;a2{-6yii?t$)2!<{ir?F!;$D+8aBUz*EW zOX5q#F8JI*`Ims63q=zFItCZdkbqUL4{&+(B_S>Cf#8jJCtjc8q6PTTdqwY513lE7gEPXMG%^$=(>B_a^>e#pmhQ>0j z$@M+-_?DfCySGcqUbTIM-J!O3^bToik1!zb@7yKblWN_)mAzYQ9*C-4iMFxUo!uKl zIKHKx74W4Qp9O~Y%6O*OwGdy=W1?T*EkPxF89e0j$SCBGV)Mty0GavSp~N)@ZWt~L znKcKvW%J;cPe;9f7le~nCx!Nm>={!PlM2k_GqKj0f(3=-z%oiJV6BjYdf{TMHNd`r zwd}MqV5B_O&R~LS(V_w=<;2>QCb9b4Jy6lY3y#Mn*jx}}9P!aqA->oV5tg8rFH{8> z%_>1!){iEkrdl$=HyV?smms6*Z?A=l>v-p5h?(2aA|vZ>Z6=Sm(^iZeM_V{&NWAWFzrpZ%=xCB zg;#jR7;W3_Uf}qZ^#YH(Ts*`6zZv)sGy~t9tiGILzed@p(^(XAu+cL!*~l)xoYIb= z{;`Uiu8N!tE)twrKRXwRFXe;bbGl#DUl<_68#K|>Tn@64FBbM<%wPdNCOSz#Qe;_* zIJhxSJ`+6eARpc$A1*35$>-}N#~Pb>_&Lb&`YRwuR63MyyA*$2st=iszZT~D5KLRH zhfZhC{-zsf7dB}?1Ri1#1rDF_q`;A?0x6I|0Rdl;@V^vRhVN7@cN4YKyX7$nDlVrE z**v!tbRs`D%UrG=frKnNaWJf(7$7Tf|D2$5tmGh>0xiV~o|mjq3>8&UMhlSrR~?Xj zk4*Zu%}_>`b!AbLBPVAFpgHvq<;cB)@Gfjdy;^^)zqzA9%>9C_u=-U4_$8nxe_H0|D{Nj z7?$Sr<^!_dINN_y9;Ay|kR{zP_Xm`be^o@EIKyrO;9HfTmR1JTQ7J{i8!5 zcqxgNL?NZuJ^%@CD#i<#u}#EqBfA!)EWw zF2!or-1M$&1u==cvh^4`@rY5>7!A}Uh&6<#{}dhxqUiWfrleCoH2J)@2{W(Via0fk z;9vsRvmOWBVKI%e2RF6(R{(0uix%a_1@Ds!W z9&RVe2L03_c(H)(nb)#^_hcWgBM*6M-6yI(bV2XLdZ$2l|Dh;Lk3&3;JPRKDuc$RMVC#&as+kSi;ts zVKbl|@9i-?;90X2zf3brdyiOII&s^SMEb2Pb);KC%R~!VDvDf6(TFI`X@}pb2>4T>BU$kj{v5|d1<9dllCx`? znn_%hM`mgod>#HUi>9d!HHMITs=eql;(Z`%QF7`QpO;fbNlamqTQ*EWzp)wi#N?OsY@7*?UPp%rAGH=BMBenFZ_q~f;ADiPUMBFZ(PM1a@( zTOmmD;uQ-K8P}VFRr>2oN*yZQ@^8OAAOALAqvAZZItt3q!LJZ(3Ynj@kg&;CBW6As z42B+lCroP3qS5=q=*+1q^vwN9;1N9-2IZR6C)bz*EI6&t*#*F?O|Jghd1zQ=+pP2I zaBPRy2?~Wq$Ju0ySMTr&b2z+?Z7AkgUWMhYN%ZbB%h2s(Ucdc^+Mjf4az^jWPdY|Q zhMj*?{=<%QFY5!L1(dAFat7jCXY)0d8>UGZgA6QiG|0jI=w_1~Jm<5&ehNM)w%Eae T?*(>{rm=&-sFZ=vhKBh!yO#<8 delta 3798 zcmeHKYitzP6~6b*F6&*}_^~#A0A8CI0by<3JCA*thapfK3^pbNFlmHmXJ^0!Y-3DG zswgZ`oQKpu^s2cENsB2U(ohA=M%lKhf~g`^iTbc3B|?-ILz}8<)ruA=suTgeGqXE8 ztMaG+J3sc^J9lQzJ?DPsJKv@!1L;b#-F zqMvMCM&H_7=8xidL|5cwU`jn*K_6C>HvW(AWFCe}I@#_aSj8F!VJ+=_eI@(nXD~>o z)^=9~008Se?}BmxOjgZR-3~H&3u7Az#@3G<66T1pL%4;|7IDIS!biW;ot3=Fp6Li92Y zkB?}HlpYO_XxhOTEQ zA{rhW8R2t+HOsNA#S4B#nHcoit{_i~%dKR&Rn@r5t&HDs+(y{^9@tK|P0W)>ERuH^ z%Q=+t4oS>(C>knpIaCQfyrZ_=b;feUaz~=7=JJOlR~&y)R=FS6(aKFhHlf2xw!05i zcnBdHy}dD5D#a8re02Ee5j{oI6N_r2u1KPGBr%a*e*MRlR{VTNDH16s*pQj`hmf^( zbQUnV?o>X_QWBE#{9zTp^1=#Yr&3H|{-JzgIXE!@#b2YN`&Y8M9(2>Qz2YUvKIE{R z_p#>y0*g#kE@Q(H80ymUkb5LFNq$v!AS@T)Dm$x65nO)+KcM$NSwaWpYASuY!DoU@ zv~%B~6cQxn@fyq3o;!K5LhU7uq@)nVFl#S!|9I&(Kgqq2qE($W?8^gC4mwpEAy(Rx zhRUDsviRkI=PLL+6u{>d>EIpqi$S;{K@p7qR@&WJPE8SwfI+^jeY!NMYzk&0O6 zmXF=siGDy=4~(+rFJRMh**aN%95H?zQ44n1bsQ!9tNiLZl?i<3MPZq<^+F!_jvby+ zBPkrk5y_7AR-l>g+Eq{`$6?1MG@?uHUhGl4tv10Y+w4*fX;GD(408Uv`$`$x+zV?E zXOc?>e3$;C`+c_Ld+@Z$u*aoQBL?eR?H*JJ_tdyja9COi&*E2 z$d~DxXoRI7IOLL~hi}xIb;8z!V96>HOrqaQZh0~fW6 z1z5>Ro7xm5;-R0tEhz;qYE}@cCJQC0OG7n!Oah3Kz z5o}jT!KY$34Ly}|yOf-!p%A+@g;v9wyi+a0&KyS#4YKW3#)TH4ia4gFEQGbWz1StL z8txu}V@{7yMB3Ng)}g0zL2*@MCpD7m5@Nnf$X2-)2bvG8jx)Q%Dtv5qLxRN^`|VD2 z5fPcL9az{T=gF9;9ESGHgs(}>vdmss0~-WBpdSr1K9&p6<^~SS0xDmzP=>zRGo|hb z*eH|oD04L{MJk{y9wkX5l`=`Lmp)kQr@x%2rvIAYJh9p42}_Qg^o+0%FT=y#~eb2a!)0k#6~ zA$7D}^jW{UoU;l$F^0ZR7oE8UW!AqQHbXgkX&l`~^uf8t3SAa>206)z3o1RJ(Na)d zPWyRen<}&nM#5N`1c~eMTEOqL^)=iHGbX(2jRny9RBQAo8t(pq; z5K(YajaVv*9iN0{EsvB>g-VeBFQ5KfK0W4E0FHbrqLQ<$U*|oD6G*wms9fNVWY0{( zy%kexx`L)Jl`NO@rK1+=mn%}I$Hom3H> zTM%POmU#-bEVuQo8zJ0_5#>;_#bF{9H^dkrrrxc?F1^dgId3YLi5Hu&Ez#vDiJLkx zC)jyoi?t!+8tb|X`&;~+IMM)Yxo%nH-1usbU{P8lk;|KV^#VduA_YWC~xXnl>tf>^UQE8@8M*oG&~e>^8WD**CA!1WaX z{W}5pn%ga30Ep7Ih35RaHHQ~YSOp#qs_E6GyV3TA>BLa&EB7b!twtAQk`-g diff --git a/src/main/java/ru/ip/labs/labs/LabsApplication.java b/src/main/java/ru/ip/labs/labs/LabsApplication.java index b7e5532..ef8df06 100644 --- a/src/main/java/ru/ip/labs/labs/LabsApplication.java +++ b/src/main/java/ru/ip/labs/labs/LabsApplication.java @@ -5,4 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class LabsApplication { + public static void main(String[] args) { + SpringApplication.run(LabsApplication.class, args); + } } diff --git a/src/main/java/ru/ip/labs/labs/configuration/PasswordEncoderConfiguration.java b/src/main/java/ru/ip/labs/labs/configuration/PasswordEncoderConfiguration.java new file mode 100644 index 0000000..5aef121 --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/configuration/PasswordEncoderConfiguration.java @@ -0,0 +1,14 @@ +package ru.ip.labs.labs.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class PasswordEncoderConfiguration { + @Bean + public PasswordEncoder createPasswordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/ru/ip/labs/labs/configuration/SecurityConfiguration.java b/src/main/java/ru/ip/labs/labs/configuration/SecurityConfiguration.java new file mode 100644 index 0000000..ec3ec4a --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/configuration/SecurityConfiguration.java @@ -0,0 +1,67 @@ +package ru.ip.labs.labs.configuration; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +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 ru.ip.labs.labs.films.controller.UserSignupMvcController; +import ru.ip.labs.labs.films.models.UserRole; +import ru.ip.labs.labs.films.service.UserService; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(securedEnabled = true) +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class); + private static final String LOGIN_URL = "/login"; + private final UserService userService; + + public SecurityConfiguration(UserService userService) { + this.userService = userService; + createAdminOnStartup(); + } + + private void createAdminOnStartup() { + final String admin = "admin"; + if (userService.findByLogin(admin) == null) { + log.info("Admin user successfully created"); + userService.createUser(admin, admin, admin, UserRole.ADMIN); + } + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.headers().frameOptions().sameOrigin().and() + .cors().and() + .csrf().disable() + .authorizeRequests() + .antMatchers(UserSignupMvcController.SIGNUP_URL).permitAll() + .antMatchers(HttpMethod.GET, LOGIN_URL).permitAll() + .anyRequest().authenticated() + .and() + .formLogin() + .loginPage(LOGIN_URL).permitAll() + .and() + .logout().permitAll(); + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userService); + } + + @Override + public void configure(WebSecurity web) { + web.ignoring() + .antMatchers("/css/**") + .antMatchers("/js/**") + .antMatchers("/templates/**") + .antMatchers("/webjars/**"); + } +} \ No newline at end of file diff --git a/src/main/java/ru/ip/labs/labs/WebConfiguration.java b/src/main/java/ru/ip/labs/labs/configuration/WebConfiguration.java similarity index 92% rename from src/main/java/ru/ip/labs/labs/WebConfiguration.java rename to src/main/java/ru/ip/labs/labs/configuration/WebConfiguration.java index f01a266..56c9ee5 100644 --- a/src/main/java/ru/ip/labs/labs/WebConfiguration.java +++ b/src/main/java/ru/ip/labs/labs/configuration/WebConfiguration.java @@ -1,4 +1,4 @@ -package ru.ip.labs.labs; +package ru.ip.labs.labs.configuration; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; @@ -15,6 +15,7 @@ public class WebConfiguration implements WebMvcConfigurer { registry.addViewController("films"); registry.addViewController("contacts"); registry.addViewController("catalogs"); + registry.addViewController("login"); } @Override public void addCorsMappings(CorsRegistry registry) { diff --git a/src/main/java/ru/ip/labs/labs/films/controller/ActorController.java b/src/main/java/ru/ip/labs/labs/films/controller/ActorController.java index 20fbd2e..c7ea2c2 100644 --- a/src/main/java/ru/ip/labs/labs/films/controller/ActorController.java +++ b/src/main/java/ru/ip/labs/labs/films/controller/ActorController.java @@ -1,7 +1,7 @@ package ru.ip.labs.labs.films.controller; import org.springframework.web.bind.annotation.*; -import ru.ip.labs.labs.WebConfiguration; +import ru.ip.labs.labs.configuration.WebConfiguration; import ru.ip.labs.labs.films.dto.ActorDTO; import ru.ip.labs.labs.films.service.ActorService; diff --git a/src/main/java/ru/ip/labs/labs/films/controller/FilmController.java b/src/main/java/ru/ip/labs/labs/films/controller/FilmController.java index d847926..3c6c96a 100644 --- a/src/main/java/ru/ip/labs/labs/films/controller/FilmController.java +++ b/src/main/java/ru/ip/labs/labs/films/controller/FilmController.java @@ -1,14 +1,11 @@ package ru.ip.labs.labs.films.controller; import org.springframework.web.bind.annotation.*; -import ru.ip.labs.labs.WebConfiguration; +import ru.ip.labs.labs.configuration.WebConfiguration; import ru.ip.labs.labs.films.dto.FilmDTO; -import ru.ip.labs.labs.films.models.Film; -import ru.ip.labs.labs.films.models.Genre; import ru.ip.labs.labs.films.service.FilmsService; import javax.validation.Valid; -import java.util.Iterator; import java.util.List; @RestController diff --git a/src/main/java/ru/ip/labs/labs/films/controller/GenreController.java b/src/main/java/ru/ip/labs/labs/films/controller/GenreController.java index b8d9df8..52945fb 100644 --- a/src/main/java/ru/ip/labs/labs/films/controller/GenreController.java +++ b/src/main/java/ru/ip/labs/labs/films/controller/GenreController.java @@ -1,12 +1,8 @@ package ru.ip.labs.labs.films.controller; import org.springframework.web.bind.annotation.*; -import ru.ip.labs.labs.WebConfiguration; -import ru.ip.labs.labs.films.dto.FilmDTO; +import ru.ip.labs.labs.configuration.WebConfiguration; import ru.ip.labs.labs.films.dto.GenreDTO; -import ru.ip.labs.labs.films.models.Film; -import ru.ip.labs.labs.films.models.Genre; -import ru.ip.labs.labs.films.service.FilmsService; import ru.ip.labs.labs.films.service.GenreService; import javax.validation.Valid; diff --git a/src/main/java/ru/ip/labs/labs/films/controller/UserMvcController.java b/src/main/java/ru/ip/labs/labs/films/controller/UserMvcController.java new file mode 100644 index 0000000..e8dfc78 --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/films/controller/UserMvcController.java @@ -0,0 +1,42 @@ +package ru.ip.labs.labs.films.controller; + +import org.springframework.data.domain.Page; +import org.springframework.security.access.annotation.Secured; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import ru.ip.labs.labs.films.dto.UserDto; +import ru.ip.labs.labs.films.models.UserRole; +import ru.ip.labs.labs.films.service.UserService; + +import java.util.List; +import java.util.stream.IntStream; + +@Controller +@RequestMapping("/users") +public class UserMvcController { + private final UserService userService; + + public UserMvcController(UserService userService) { + this.userService = userService; + } + + @GetMapping + @Secured({UserRole.AsString.ADMIN}) + public String getUsers(@RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "5") int size, + Model model) { + final Page users = userService.findAllPages(page, size) + .map(UserDto::new); + model.addAttribute("users", users); + final int totalPages = users.getTotalPages(); + final List pageNumbers = IntStream.rangeClosed(1, totalPages) + .boxed() + .toList(); + model.addAttribute("pages", pageNumbers); + model.addAttribute("totalPages", totalPages); + return "users"; + } +} diff --git a/src/main/java/ru/ip/labs/labs/films/controller/UserSignupMvcController.java b/src/main/java/ru/ip/labs/labs/films/controller/UserSignupMvcController.java new file mode 100644 index 0000000..bb842b5 --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/films/controller/UserSignupMvcController.java @@ -0,0 +1,51 @@ +package ru.ip.labs.labs.films.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import ru.ip.labs.labs.films.models.User; +import ru.ip.labs.labs.films.models.UserSignupDto; +import ru.ip.labs.labs.films.service.UserService; +import ru.ip.labs.labs.films.util.validation.ValidationException; + +import javax.validation.Valid; + +@Controller +@RequestMapping(UserSignupMvcController.SIGNUP_URL) +public class UserSignupMvcController { + public static final String SIGNUP_URL = "/signup"; + + private final UserService userService; + + public UserSignupMvcController(UserService userService) { + this.userService = userService; + } + + @GetMapping + public String showSignupForm(Model model) { + model.addAttribute("userDto", new UserSignupDto()); + return "signup"; + } + + @PostMapping + public String signup(@ModelAttribute("userDto") @Valid UserSignupDto userSignupDto, + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + model.addAttribute("errors", bindingResult.getAllErrors()); + return "signup"; + } + try { + final User user = userService.createUser( + userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm()); + return "redirect:/login?created=" + user.getLogin(); + } catch (ValidationException e) { + model.addAttribute("errors", e.getMessage()); + return "signup"; + } + } +} diff --git a/src/main/java/ru/ip/labs/labs/films/dto/UserDto.java b/src/main/java/ru/ip/labs/labs/films/dto/UserDto.java new file mode 100644 index 0000000..7c20374 --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/films/dto/UserDto.java @@ -0,0 +1,28 @@ +package ru.ip.labs.labs.films.dto; + +import ru.ip.labs.labs.films.models.User; +import ru.ip.labs.labs.films.models.UserRole; + +public class UserDto { + private final long id; + private final String login; + private final UserRole role; + + public UserDto(User user) { + this.id = user.getId(); + this.login = user.getLogin(); + this.role = user.getRole(); + } + + public long getId() { + return id; + } + + public String getLogin() { + return login; + } + + public UserRole getRole() { + return role; + } +} diff --git a/src/main/java/ru/ip/labs/labs/films/models/User.java b/src/main/java/ru/ip/labs/labs/films/models/User.java new file mode 100644 index 0000000..b48b26a --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/films/models/User.java @@ -0,0 +1,73 @@ +package ru.ip.labs.labs.films.models; + +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.util.Objects; + +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + @Column(nullable = false, unique = true, length = 64) + @NotBlank + @Size(min = 3, max = 64) + private String login; + @Column(nullable = false, length = 64) + @NotBlank + @Size(min = 6, max = 64) + private String password; + private UserRole role; + + public User() { + } + + public User(String login, String password) { + this(login, password, UserRole.USER); + } + + public User(String login, String password, UserRole role) { + this.login = login; + this.password = password; + this.role = role; + } + + public Long getId() { + return id; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public UserRole getRole() { + return role; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return Objects.equals(id, user.id) && Objects.equals(login, user.login); + } + + @Override + public int hashCode() { + return Objects.hash(id, login); + } +} diff --git a/src/main/java/ru/ip/labs/labs/films/models/UserRole.java b/src/main/java/ru/ip/labs/labs/films/models/UserRole.java new file mode 100644 index 0000000..6aa4498 --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/films/models/UserRole.java @@ -0,0 +1,20 @@ +package ru.ip.labs.labs.films.models; + +import org.springframework.security.core.GrantedAuthority; + +public enum UserRole implements GrantedAuthority { + ADMIN, + USER; + + private static final String PREFIX = "ROLE_"; + + @Override + public String getAuthority() { + return PREFIX + this.name(); + } + + public static final class AsString { + public static final String ADMIN = PREFIX + "ADMIN"; + public static final String USER = PREFIX + "USER"; + } +} diff --git a/src/main/java/ru/ip/labs/labs/films/models/UserSignupDto.java b/src/main/java/ru/ip/labs/labs/films/models/UserSignupDto.java new file mode 100644 index 0000000..48623fe --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/films/models/UserSignupDto.java @@ -0,0 +1,40 @@ +package ru.ip.labs.labs.films.models; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +public class UserSignupDto { + @NotBlank + @Size(min = 3, max = 64) + private String login; + @NotBlank + @Size(min = 6, max = 64) + private String password; + @NotBlank + @Size(min = 6, max = 64) + private String passwordConfirm; + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getPasswordConfirm() { + return passwordConfirm; + } + + public void setPasswordConfirm(String passwordConfirm) { + this.passwordConfirm = passwordConfirm; + } +} diff --git a/src/main/java/ru/ip/labs/labs/films/repository/UserRepository.java b/src/main/java/ru/ip/labs/labs/films/repository/UserRepository.java new file mode 100644 index 0000000..9df007c --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/films/repository/UserRepository.java @@ -0,0 +1,8 @@ +package ru.ip.labs.labs.films.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ip.labs.labs.films.models.User; + +public interface UserRepository extends JpaRepository { + User findOneByLoginIgnoreCase(String login); +} diff --git a/src/main/java/ru/ip/labs/labs/films/service/UserService.java b/src/main/java/ru/ip/labs/labs/films/service/UserService.java new file mode 100644 index 0000000..247031b --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/films/service/UserService.java @@ -0,0 +1,67 @@ +package ru.ip.labs.labs.films.service; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import ru.ip.labs.labs.films.models.User; +import ru.ip.labs.labs.films.models.UserRole; +import ru.ip.labs.labs.films.repository.UserRepository; +import ru.ip.labs.labs.films.util.validation.ValidationException; +import ru.ip.labs.labs.films.util.validation.ValidatorUtil; + +import java.util.Collections; +import java.util.Objects; + +@Service +public class UserService implements UserDetailsService { + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + private final ValidatorUtil validatorUtil; + + public UserService(UserRepository userRepository, + PasswordEncoder passwordEncoder, + ValidatorUtil validatorUtil) { + this.userRepository = userRepository; + this.passwordEncoder = passwordEncoder; + this.validatorUtil = validatorUtil; + } + + public Page findAllPages(int page, int size) { + return userRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending())); + } + + public User findByLogin(String login) { + return userRepository.findOneByLoginIgnoreCase(login); + } + + public User createUser(String login, String password, String passwordConfirm) { + return createUser(login, password, passwordConfirm, UserRole.USER); + } + + public User createUser(String login, String password, String passwordConfirm, UserRole role) { + if (findByLogin(login) != null) { + throw new ValidationException(String.format("User '%s' already exists", login)); + } + final User user = new User(login, passwordEncoder.encode(password), role); + validatorUtil.validate(user); + if (!Objects.equals(password, passwordConfirm)) { + throw new ValidationException("Passwords not equals"); + } + return userRepository.save(user); + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + final User userEntity = findByLogin(username); + if (userEntity == null) { + throw new UsernameNotFoundException(username); + } + return new org.springframework.security.core.userdetails.User( + userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole())); + } +} diff --git a/src/main/java/ru/ip/labs/labs/films/util/error/AdviceController.java b/src/main/java/ru/ip/labs/labs/films/util/error/AdviceController.java new file mode 100644 index 0000000..5413939 --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/films/util/error/AdviceController.java @@ -0,0 +1,37 @@ +package ru.ip.labs.labs.films.util.error; + +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import ru.ip.labs.labs.films.util.validation.ValidationException; + +import java.util.stream.Collectors; + +@ControllerAdvice(annotations = RestController.class) +public class AdviceController { + @ExceptionHandler({ + ValidationException.class + }) + public ResponseEntity handleException(Throwable e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleBindException(MethodArgumentNotValidException e) { + final ValidationException validationException = new ValidationException( + e.getBindingResult().getAllErrors().stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.toSet())); + return handleException(validationException); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleUnknownException(Throwable e) { + e.printStackTrace(); + return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/src/main/java/ru/ip/labs/labs/films/util/validation/ValidationException.java b/src/main/java/ru/ip/labs/labs/films/util/validation/ValidationException.java new file mode 100644 index 0000000..be0282f --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/films/util/validation/ValidationException.java @@ -0,0 +1,13 @@ +package ru.ip.labs.labs.films.util.validation; + +import java.util.Set; + +public class ValidationException extends RuntimeException { + public ValidationException(Set errors) { + super(String.join("\n", errors)); + } + + public ValidationException(String error) { + super(error); + } +} diff --git a/src/main/java/ru/ip/labs/labs/films/util/validation/ValidatorUtil.java b/src/main/java/ru/ip/labs/labs/films/util/validation/ValidatorUtil.java new file mode 100644 index 0000000..c7cd72a --- /dev/null +++ b/src/main/java/ru/ip/labs/labs/films/util/validation/ValidatorUtil.java @@ -0,0 +1,27 @@ +package ru.ip.labs.labs.films.util.validation; + +import org.springframework.stereotype.Component; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import java.util.Set; +import java.util.stream.Collectors; + +@Component +public class ValidatorUtil { + private final Validator validator; + + public ValidatorUtil() { + this.validator = Validation.buildDefaultValidatorFactory().getValidator(); + } + + public void validate(T object) { + final Set> errors = validator.validate(object); + if (!errors.isEmpty()) { + throw new ValidationException(errors.stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.toSet())); + } + } +} diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index 1573168..5a18284 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -1,6 +1,7 @@ @@ -23,7 +24,7 @@ diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..019f575 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,30 @@ + + + +
+
+ Пользователь не найден или пароль указан не верно +
+
+ Выход успешно произведен +
+
+ Пользователь '' успешно создан +
+
+
+ +
+
+ +
+ + Регистрация +
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/signup.html b/src/main/resources/templates/signup.html new file mode 100644 index 0000000..8cd75f5 --- /dev/null +++ b/src/main/resources/templates/signup.html @@ -0,0 +1,28 @@ + + + +
+
+
+
+ +
+
+ +
+
+ +
+
+ + Назад +
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/users.html b/src/main/resources/templates/users.html new file mode 100644 index 0000000..7a21a02 --- /dev/null +++ b/src/main/resources/templates/users.html @@ -0,0 +1,37 @@ + + + +
+
+ + + + + + + + + + + + + + + + + +
#IDЛогинРоль
+
+ +
+ + \ No newline at end of file