Skip to content

Commit ca56119

Browse files
committed
Recovery password
1 parent 7a825dd commit ca56119

File tree

11 files changed

+432
-156
lines changed

11 files changed

+432
-156
lines changed

src/main/java/com/github/throyer/common/springboot/controllers/api/RecoveriesController.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.github.throyer.common.springboot.controllers.api;
22

3+
import com.github.throyer.common.springboot.domain.services.recovery.RecoveryConfirmService;
4+
import com.github.throyer.common.springboot.domain.services.recovery.RecoveryService;
5+
import com.github.throyer.common.springboot.domain.services.recovery.RecoveryUpdateService;
36
import static org.springframework.http.HttpStatus.NO_CONTENT;
47

5-
import com.github.throyer.common.springboot.domain.services.user.RecoveryPasswordService;
68
import com.github.throyer.common.springboot.domain.services.user.dto.RecoveryConfirm;
79
import com.github.throyer.common.springboot.domain.services.user.dto.RecoveryRequest;
810
import com.github.throyer.common.springboot.domain.services.user.dto.RecoveryUpdate;
@@ -22,22 +24,28 @@
2224
public class RecoveriesController {
2325

2426
@Autowired
25-
private RecoveryPasswordService service;
27+
private RecoveryService recoveryService;
28+
29+
@Autowired
30+
private RecoveryConfirmService confirmService;
31+
32+
@Autowired
33+
private RecoveryUpdateService updateService;
2634

2735
@PostMapping
2836
@ResponseStatus(NO_CONTENT)
2937
public void index(@RequestBody RecoveryRequest request) {
30-
service.recovery(request.getEmail());
38+
recoveryService.recovery(request.getEmail());
3139
}
3240

3341
@PostMapping("/confirm")
3442
public void confirm(@RequestBody RecoveryConfirm confirm) {
35-
service.confirm(confirm.getEmail(), confirm.getCode());
43+
confirmService.confirm(confirm.getEmail(), confirm.getCode());
3644
}
3745

3846
@PostMapping("/update")
3947
@ResponseStatus(NO_CONTENT)
4048
public void update(@RequestBody RecoveryUpdate update) {
41-
service.update(update.getEmail(), update.getCode(), update.getPassword());
49+
updateService.update(update.getEmail(), update.getCode(), update.getPassword());
4250
}
4351
}
Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.github.throyer.common.springboot.controllers.app;
22

3-
import com.github.throyer.common.springboot.domain.services.user.RecoveryPasswordService;
3+
import com.github.throyer.common.springboot.domain.services.recovery.RecoveryConfirmService;
4+
import com.github.throyer.common.springboot.domain.services.recovery.RecoveryService;
5+
import com.github.throyer.common.springboot.domain.services.recovery.RecoveryUpdateService;
46
import com.github.throyer.common.springboot.domain.services.user.dto.Codes;
57
import com.github.throyer.common.springboot.domain.services.user.dto.RecoveryRequest;
8+
import com.github.throyer.common.springboot.domain.services.user.dto.Update;
69
import javax.validation.Valid;
710
import org.springframework.beans.factory.annotation.Autowired;
811
import org.springframework.stereotype.Controller;
@@ -11,41 +14,65 @@
1114
import org.springframework.web.bind.annotation.GetMapping;
1215
import org.springframework.web.bind.annotation.PostMapping;
1316
import org.springframework.web.bind.annotation.RequestMapping;
17+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
1418

1519
@Controller
1620
@RequestMapping("/app/recovery")
1721
public class RecoveryController {
1822

1923
@Autowired
20-
private RecoveryPasswordService service;
21-
24+
private RecoveryService recoveryService;
25+
26+
@Autowired
27+
private RecoveryConfirmService confirmService;
28+
29+
@Autowired
30+
private RecoveryUpdateService updateService;
31+
2232
@GetMapping
2333
public String index(Model model) {
2434
model.addAttribute("recovery", new RecoveryRequest());
2535
return "/app/recovery/index";
2636
}
2737

2838
@PostMapping
29-
public String create(
39+
public String index(
3040
@Valid RecoveryRequest recovery,
3141
BindingResult result,
3242
Model model
3343
) {
34-
return service.recovery(recovery, result, model);
44+
return recoveryService.recovery(recovery, result, model);
3545
}
3646

37-
@GetMapping("/confirm")
38-
public String codes(Model model) {
39-
model.addAttribute("codes", new Codes());
40-
return "/app/recovery/confirm";
41-
}
47+
// @GetMapping("/confirm")
48+
// public String confirm(Model model) {
49+
// model.addAttribute("codes", new Codes());
50+
// return "/app/recovery/confirm";
51+
// }
4252

4353
@PostMapping("/confirm")
4454
public String confirm(
4555
@Valid Codes codes,
4656
BindingResult result,
57+
RedirectAttributes redirect,
58+
Model model
59+
) {
60+
return confirmService.confirm(codes, result, model, redirect);
61+
}
62+
63+
// @GetMapping("/update")
64+
// public String update(Model model) {
65+
// model.addAttribute("update", new Update());
66+
// return "/app/recovery/update";
67+
// }
68+
69+
@PostMapping("/update")
70+
public String update(
71+
@Valid Update update,
72+
BindingResult result,
73+
RedirectAttributes redirect,
4774
Model model
4875
) {
49-
return service.confirm(codes, result, model);
76+
return updateService.update(update, result, model, redirect);
5077
}
5178
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.github.throyer.common.springboot.domain.services.recovery;
2+
3+
import com.github.throyer.common.springboot.domain.models.shared.Type;
4+
import com.github.throyer.common.springboot.domain.repositories.RecoveryRepository;
5+
import com.github.throyer.common.springboot.domain.repositories.UserRepository;
6+
import com.github.throyer.common.springboot.domain.services.user.dto.Codes;
7+
import com.github.throyer.common.springboot.domain.services.user.dto.Update;
8+
import com.github.throyer.common.springboot.utils.Toasts;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.http.HttpStatus;
11+
import org.springframework.stereotype.Service;
12+
import org.springframework.ui.Model;
13+
import org.springframework.validation.BindingResult;
14+
import org.springframework.web.server.ResponseStatusException;
15+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
16+
17+
@Service
18+
public class RecoveryConfirmService {
19+
20+
@Autowired
21+
private UserRepository users;
22+
23+
@Autowired
24+
private RecoveryRepository recoveries;
25+
26+
public String confirm(Codes codes, BindingResult result, Model model, RedirectAttributes redirect) {
27+
28+
if (result.hasErrors()) {
29+
Toasts.add(model, result);
30+
model.addAttribute("confirm", codes);
31+
return "/app/recovery/confirm";
32+
}
33+
34+
try {
35+
36+
confirm(codes.getEmail(), codes.code());
37+
return "redirect:/app/recovery/update";
38+
39+
} catch (ResponseStatusException exception) {
40+
41+
if (exception.getStatus().equals(HttpStatus.FORBIDDEN)) {
42+
43+
Toasts.add(model, "Código expirado ou invalido.", Type.DANGER);
44+
model.addAttribute("confirm", codes);
45+
46+
return "/app/recovery/confirm";
47+
}
48+
49+
model.addAttribute("update", new Update(codes));
50+
51+
return "/app/recovery/update";
52+
}
53+
}
54+
55+
public void confirm(String email, String code) {
56+
var user = users.findOptionalByEmail(email)
57+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.FORBIDDEN));
58+
59+
var recovery = recoveries
60+
.findFirstOptionalByUser_IdAndConfirmedIsFalseAndUsedIsFalseOrderByExpiresInDesc(user.getId())
61+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.FORBIDDEN));
62+
63+
if (!recovery.nonExpired() || !recovery.getCode().equals(code)) {
64+
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
65+
}
66+
67+
recovery.setConfirmed(true);
68+
69+
recoveries.save(recovery);
70+
71+
throw new ResponseStatusException(HttpStatus.NO_CONTENT);
72+
}
73+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.github.throyer.common.springboot.domain.services.recovery;
2+
3+
import com.github.throyer.common.springboot.domain.models.emails.RecoveryEmail;
4+
import com.github.throyer.common.springboot.domain.models.entity.Recovery;
5+
import com.github.throyer.common.springboot.domain.repositories.RecoveryRepository;
6+
import com.github.throyer.common.springboot.domain.repositories.UserRepository;
7+
import com.github.throyer.common.springboot.domain.services.email.MailService;
8+
import com.github.throyer.common.springboot.domain.services.user.dto.Codes;
9+
import com.github.throyer.common.springboot.domain.services.user.dto.RecoveryRequest;
10+
import com.github.throyer.common.springboot.utils.Toasts;
11+
import org.springframework.beans.factory.annotation.Autowired;
12+
import org.springframework.stereotype.Service;
13+
import org.springframework.ui.Model;
14+
import org.springframework.validation.BindingResult;
15+
16+
@Service
17+
public class RecoveryService {
18+
19+
@Autowired
20+
private UserRepository users;
21+
22+
@Autowired
23+
private RecoveryRepository recoveries;
24+
25+
@Autowired
26+
private MailService service;
27+
28+
public String recovery(RecoveryRequest recovery, BindingResult result, Model model) {
29+
30+
if (result.hasErrors()) {
31+
Toasts.add(model, result);
32+
model.addAttribute("recovery", recovery);
33+
return "/app/recovery/index";
34+
}
35+
36+
var email = recovery.getEmail();
37+
38+
recovery(email);
39+
40+
model.addAttribute("codes", new Codes(email));
41+
42+
return "/app/recovery/confirm";
43+
}
44+
45+
public void recovery(String email) {
46+
var user = users.findOptionalByEmail(email);
47+
48+
if (user.isEmpty()) {
49+
return;
50+
}
51+
52+
var minutesToExpire = 20;
53+
54+
var recovery = new Recovery(user.get(), minutesToExpire);
55+
56+
recoveries.save(recovery);
57+
58+
try {
59+
var recoveryEmail = new RecoveryEmail(email, "password recovery code", recovery.getCode());
60+
service.send(recoveryEmail);
61+
} catch (Exception exception) { }
62+
}
63+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.github.throyer.common.springboot.domain.services.recovery;
2+
3+
import com.github.throyer.common.springboot.domain.models.shared.Type;
4+
import com.github.throyer.common.springboot.domain.repositories.RecoveryRepository;
5+
import com.github.throyer.common.springboot.domain.repositories.UserRepository;
6+
import com.github.throyer.common.springboot.domain.services.user.dto.Update;
7+
import com.github.throyer.common.springboot.utils.Toasts;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.http.HttpStatus;
10+
import org.springframework.stereotype.Service;
11+
import org.springframework.ui.Model;
12+
import org.springframework.validation.BindingResult;
13+
import org.springframework.web.server.ResponseStatusException;
14+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
15+
16+
@Service
17+
public class RecoveryUpdateService {
18+
19+
@Autowired
20+
private UserRepository users;
21+
22+
@Autowired
23+
private RecoveryRepository recoveries;
24+
25+
public String update(
26+
Update update,
27+
BindingResult result,
28+
Model model,
29+
RedirectAttributes redirect
30+
) {
31+
32+
update.validate(result);
33+
34+
if (result.hasErrors()) {
35+
Toasts.add(model, result);
36+
model.addAttribute("update", update);
37+
return "/app/recovery/update";
38+
}
39+
40+
try {
41+
update(update.getEmail(), update.code(), update.getPassword());
42+
return "redirect:/app/login";
43+
} catch (ResponseStatusException exception) {
44+
if (exception.getStatus().equals(HttpStatus.FORBIDDEN)) {
45+
Toasts.add(model, "Código expirado ou invalido.", Type.DANGER);
46+
model.addAttribute("update", update);
47+
return "/app/recovery/update";
48+
}
49+
50+
Toasts.add(redirect, "Sua senha foi atualizada com sucesso.", Type.SUCCESS);
51+
return "redirect:/app/login";
52+
}
53+
}
54+
55+
public void update(String email, String code, String password) {
56+
var user = users.findOptionalByEmail(email)
57+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.FORBIDDEN));
58+
59+
var recovery = recoveries
60+
.findFirstOptionalByUser_IdAndConfirmedIsTrueAndUsedIsFalseOrderByExpiresInDesc(user.getId())
61+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.FORBIDDEN));
62+
63+
if (!recovery.nonExpired() || !recovery.getCode().equals(code)) {
64+
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
65+
}
66+
67+
user.updatePassword(password);
68+
users.save(user);
69+
70+
recovery.setUsed(true);
71+
recoveries.save(recovery);
72+
}
73+
}

0 commit comments

Comments
 (0)