package io.kaif.web.api; import java.util.Locale; import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import org.hibernate.validator.constraints.Email; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import io.kaif.flake.FlakeId; import io.kaif.model.account.Account; import io.kaif.model.account.AccountAccessToken; import io.kaif.model.account.AccountAuth; import io.kaif.model.exception.AuthenticateFailException; import io.kaif.service.AccountService; import io.kaif.service.FeedService; import io.kaif.web.support.SingleWrapper; @RestController @RequestMapping("/api/account") public class AccountResource { static class AccountRequest { @Size(min = Account.NAME_MIN, max = Account.NAME_MAX) @NotNull @Pattern(regexp = Account.NAME_PATTERN) public String username; @Size(min = Account.PASSWORD_MIN, max = Account.PASSWORD_MAX) @NotNull public String password; @Email @NotNull public String email; } static class Credential { @NotNull public String username; @NotNull public String password; } static class SendResetPasswordRequest { @NotNull public String username; @Email @NotNull public String email; } static class UpdatePasswordWithTokenRequest { @Size(min = Account.PASSWORD_MIN, max = Account.PASSWORD_MAX) @NotNull public String password; @NotNull public String token; } static class UpdateNewPasswordRequest { @Size(min = Account.PASSWORD_MIN, max = Account.PASSWORD_MAX) @NotNull public String oldPassword; @Size(min = Account.PASSWORD_MIN, max = Account.PASSWORD_MAX) @NotNull public String newPassword; } static class UpdateDescriptionRequest { public String description; } static class PreviewDescription { public String description; } static class NewsFeedAcknowledge { @NotNull public FlakeId assetId; } @Autowired private AccountService accountService; @Autowired private FeedService feedService; @RequestMapping(value = "/", method = RequestMethod.PUT, consumes = { MediaType.APPLICATION_JSON_VALUE }) public void create(@Valid @RequestBody AccountRequest request, Locale locale) { accountService.createViaEmail(request.username.trim(), request.email.trim(), request.password, locale); } /** * force json for better CSRF protection */ @RequestMapping(value = "/authenticate", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE }) public AccountAuth authenticate(@Valid @RequestBody Credential credential) { return accountService.authenticate(credential.username.trim(), credential.password) .orElseThrow(AuthenticateFailException::new); } @RequestMapping(value = "/extends-access-token", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE }) public AccountAuth extendsAccessToken(AccountAccessToken token) { return accountService.extendsAccessToken(token); } @RequestMapping(value = "/resend-activation", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE }) public void resendActivation(AccountAccessToken token, Locale locale) { accountService.resendActivation(token, locale); } @RequestMapping(value = "/email-available") public SingleWrapper<Boolean> isEmailAvailable(@RequestParam("email") String email) { return SingleWrapper.of(accountService.isEmailAvailable(email)); } @RequestMapping(value = "/name-available") public SingleWrapper<Boolean> isNameAvailable(@RequestParam("username") String username) { return SingleWrapper.of(accountService.isUsernameAvailable(username)); } @RequestMapping(value = "/news-feed-unread") public SingleWrapper<Integer> newsFeedUnreadCount(AccountAccessToken token) { return SingleWrapper.of(feedService.countUnread(token)); } @RequestMapping(value = "/news-feed-acknowledge", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE }) public void newsFeedAcknowledge(AccountAccessToken token, @Valid @RequestBody NewsFeedAcknowledge ack) { feedService.acknowledge(token, ack.assetId); } @RequestMapping(value = "/send-reset-password", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE }) public void sendResetPassword(@Valid @RequestBody SendResetPasswordRequest request, Locale locale) { accountService.sendResetPassword(request.username, request.email, locale); } @RequestMapping(value = "/update-password-with-token", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE }) public void updatePasswordWithOnceToken( @Valid @RequestBody UpdatePasswordWithTokenRequest request, Locale locale) { accountService.updatePasswordWithOnceToken(request.token, request.password, locale); } @RequestMapping(value = "/update-new-password", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE }) public AccountAuth updateNewPassword(AccountAccessToken accessToken, @Valid @RequestBody UpdateNewPasswordRequest request, Locale locale) { return accountService.updateNewPassword(accessToken, request.oldPassword, request.newPassword, locale); } @RequestMapping(value = "/description", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE }) public String updateDescription(AccountAccessToken accessToken, @Valid @RequestBody UpdateDescriptionRequest request) { return accountService.updateDescription(accessToken, request.description); } //this method is public, do not need authenticate @RequestMapping(value = "/description/preview", method = RequestMethod.PUT, consumes = { MediaType.APPLICATION_JSON_VALUE }) public String previewDescriptionContent(@Valid @RequestBody PreviewDescription request) { return Account.renderDescriptionPreview(request.description); } @RequestMapping(value = "/description", method = RequestMethod.GET) public String loadEditableDescription(AccountAccessToken token) { return accountService.loadEditableDescription(token); } @RequestMapping(value = "/oauth-direct-authorize-token", method = RequestMethod.PUT) public SingleWrapper<String> createOauthDirectAuthorizeToken(AccountAccessToken accountAccessToken) { return SingleWrapper.of(accountService.createOauthDirectAuthorizeToken(accountAccessToken) .getToken()); } }