Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor post and cateogry authentication #1678

Merged
merged 13 commits into from
Mar 1, 2022
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ public List<? extends CategoryDTO> listAll(
@SortDefault(sort = "priority", direction = ASC) Sort sort,
@RequestParam(name = "more", required = false, defaultValue = "false") boolean more) {
if (more) {
return postCategoryService.listCategoryWithPostCountDto(sort, true);
return postCategoryService.listCategoryWithPostCountDto(sort);
}

return categoryService.convertTo(categoryService.listAll(sort, true));
return categoryService.convertTo(categoryService.listAll(sort));
}

@GetMapping("tree_view")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package run.halo.app.controller.content;

import static run.halo.app.model.support.HaloConst.POST_PASSWORD_TEMPLATE;
import static run.halo.app.model.support.HaloConst.SUFFIX_FTL;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -16,31 +17,36 @@
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import run.halo.app.cache.lock.CacheLock;
import run.halo.app.controller.content.auth.ContentAuthenticationManager;
import run.halo.app.controller.content.auth.ContentAuthenticationRequest;
import run.halo.app.controller.content.model.CategoryModel;
import run.halo.app.controller.content.model.JournalModel;
import run.halo.app.controller.content.model.LinkModel;
import run.halo.app.controller.content.model.PhotoModel;
import run.halo.app.controller.content.model.PostModel;
import run.halo.app.controller.content.model.SheetModel;
import run.halo.app.controller.content.model.TagModel;
import run.halo.app.exception.AuthenticationException;
import run.halo.app.exception.NotFoundException;
import run.halo.app.exception.UnsupportedException;
import run.halo.app.model.dto.CategoryDTO;
import run.halo.app.model.dto.post.BasePostMinimalDTO;
import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Post;
import run.halo.app.model.entity.Sheet;
import run.halo.app.model.enums.EncryptTypeEnum;
import run.halo.app.model.enums.PostPermalinkType;
import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.enums.SheetPermalinkType;
import run.halo.app.service.AuthenticationService;
import run.halo.app.service.CategoryService;
import run.halo.app.service.OptionService;
import run.halo.app.service.PostService;
import run.halo.app.service.SheetService;
import run.halo.app.service.ThemeService;

/**
* @author ryanwang
* @author guqing
* @date 2020-01-07
*/
@Slf4j
Expand Down Expand Up @@ -68,10 +74,12 @@ public class ContentContentController {

private final SheetService sheetService;

private final AuthenticationService authenticationService;

private final CategoryService categoryService;

private final ThemeService themeService;

private final ContentAuthenticationManager providerManager;

public ContentContentController(PostModel postModel,
SheetModel sheetModel,
CategoryModel categoryModel,
Expand All @@ -82,8 +90,9 @@ public ContentContentController(PostModel postModel,
OptionService optionService,
PostService postService,
SheetService sheetService,
AuthenticationService authenticationService,
CategoryService categoryService) {
CategoryService categoryService,
ThemeService themeService,
ContentAuthenticationManager providerManager) {
this.postModel = postModel;
this.sheetModel = sheetModel;
this.categoryModel = categoryModel;
Expand All @@ -94,8 +103,9 @@ public ContentContentController(PostModel postModel,
this.optionService = optionService;
this.postService = postService;
this.sheetService = sheetService;
this.authenticationService = authenticationService;
this.categoryService = categoryService;
this.themeService = themeService;
this.providerManager = providerManager;
}

@GetMapping("{prefix}")
Expand Down Expand Up @@ -240,18 +250,60 @@ public String content(@PathVariable("year") Integer year,
@CacheLock(traceRequest = true, expired = 2)
public String password(@PathVariable("type") String type,
@PathVariable("slug") String slug,
@RequestParam(value = "password") String password) throws UnsupportedEncodingException {

String redirectUrl;

@RequestParam(value = "password") String password,
HttpServletRequest request) throws UnsupportedEncodingException {
if (EncryptTypeEnum.POST.getName().equals(type)) {
redirectUrl = doAuthenticationPost(slug, password);
return authenticatePost(slug, type, password, request);
} else if (EncryptTypeEnum.CATEGORY.getName().equals(type)) {
redirectUrl = doAuthenticationCategory(slug, password);
return authenticateCategory(slug, type, password, request);
} else {
throw new UnsupportedException("未知的加密类型");
}
return "redirect:" + redirectUrl;
}

private String authenticatePost(String slug, String type, String password,
HttpServletRequest request) {
ContentAuthenticationRequest authRequest = new ContentAuthenticationRequest();
authRequest.setPassword(password);
Post post = postService.getBy(PostStatus.INTIMATE, slug);
authRequest.setId(post.getId());
authRequest.setPrincipal(EncryptTypeEnum.POST.getName());
try {
providerManager.authenticate(authRequest);
BasePostMinimalDTO basePostMinimal = postService.convertToMinimal(post);
return "redirect:" + buildRedirectUrl(basePostMinimal.getFullPath());
} catch (AuthenticationException e) {
request.setAttribute("errorMsg", e.getMessage());
request.setAttribute("type", type);
request.setAttribute("slug", slug);
return getPasswordPageUriToForward();
}
}

private String authenticateCategory(String slug, String type, String password,
HttpServletRequest request) {
ContentAuthenticationRequest authRequest = new ContentAuthenticationRequest();
authRequest.setPassword(password);
Category category = categoryService.getBySlugOfNonNull(slug);
authRequest.setId(category.getId());
authRequest.setPrincipal(EncryptTypeEnum.CATEGORY.getName());
try {
providerManager.authenticate(authRequest);
CategoryDTO categoryDto = categoryService.convertTo(category);
return "redirect:" + buildRedirectUrl(categoryDto.getFullPath());
} catch (AuthenticationException e) {
request.setAttribute("errorMsg", e.getMessage());
request.setAttribute("type", type);
request.setAttribute("slug", slug);
return getPasswordPageUriToForward();
}
}

private String getPasswordPageUriToForward() {
if (themeService.templateExists(POST_PASSWORD_TEMPLATE + SUFFIX_FTL)) {
return themeService.render(POST_PASSWORD_TEMPLATE);
}
return "common/template/" + POST_PASSWORD_TEMPLATE;
}

private NotFoundException buildPathNotFoundException() {
Expand All @@ -265,41 +317,13 @@ private NotFoundException buildPathNotFoundException() {
return new NotFoundException("无法定位到该路径:" + requestUri);
}

private String doAuthenticationPost(
String slug, String password) throws UnsupportedEncodingException {
Post post = postService.getBy(PostStatus.INTIMATE, slug);

post.setSlug(URLEncoder.encode(post.getSlug(), StandardCharsets.UTF_8.name()));

authenticationService.postAuthentication(post, password);

BasePostMinimalDTO postMinimalDTO = postService.convertToMinimal(post);

private String buildRedirectUrl(String fullPath) {
StringBuilder redirectUrl = new StringBuilder();

if (!optionService.isEnabledAbsolutePath()) {
redirectUrl.append(optionService.getBlogBaseUrl());
}

redirectUrl.append(postMinimalDTO.getFullPath());

return redirectUrl.toString();
}

private String doAuthenticationCategory(String slug, String password) {
CategoryDTO
category = categoryService.convertTo(categoryService.getBySlugOfNonNull(slug, true));

authenticationService.categoryAuthentication(category.getId(), password);

StringBuilder redirectUrl = new StringBuilder();

if (!optionService.isEnabledAbsolutePath()) {
redirectUrl.append(optionService.getBlogBaseUrl());
}

redirectUrl.append(category.getFullPath());

redirectUrl.append(fullPath);
return redirectUrl.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,28 @@
import com.google.common.collect.Sets;
import io.swagger.annotations.ApiOperation;
import java.util.List;
import java.util.Set;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.data.web.SortDefault;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import run.halo.app.controller.content.auth.CategoryAuthentication;
import run.halo.app.controller.content.auth.ContentAuthenticationManager;
import run.halo.app.controller.content.auth.ContentAuthenticationRequest;
import run.halo.app.exception.ForbiddenException;
import run.halo.app.model.dto.CategoryDTO;
import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Post;
import run.halo.app.model.enums.EncryptTypeEnum;
import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.vo.PostListVO;
import run.halo.app.service.AuthenticationService;
import run.halo.app.service.CategoryService;
import run.halo.app.service.PostCategoryService;
import run.halo.app.service.PostService;
Expand All @@ -42,16 +47,20 @@ public class CategoryController {

private final PostService postService;

private final AuthenticationService authenticationService;
private final CategoryAuthentication categoryAuthentication;

private final ContentAuthenticationManager contentAuthenticationManager;

public CategoryController(CategoryService categoryService,
PostCategoryService postCategoryService,
PostService postService,
AuthenticationService authenticationService) {
CategoryAuthentication categoryAuthentication,
ContentAuthenticationManager contentAuthenticationManager) {
this.categoryService = categoryService;
this.postCategoryService = postCategoryService;
this.postService = postService;
this.authenticationService = authenticationService;
this.categoryAuthentication = categoryAuthentication;
this.contentAuthenticationManager = contentAuthenticationManager;
}

@GetMapping
Expand All @@ -60,7 +69,7 @@ public List<? extends CategoryDTO> listCategories(
@SortDefault(sort = "updateTime", direction = DESC) Sort sort,
@RequestParam(name = "more", required = false, defaultValue = "false") Boolean more) {
if (more) {
return postCategoryService.listCategoryWithPostCountDto(sort, false);
return postCategoryService.listCategoryWithPostCountDto(sort);
}
return categoryService.convertTo(categoryService.listAll(sort));
}
Expand All @@ -72,15 +81,37 @@ public Page<PostListVO> listPostsBy(@PathVariable("slug") String slug,
@PageableDefault(sort = {"topPriority", "updateTime"}, direction = DESC)
Pageable pageable) {
// Get category by slug
Category category = categoryService.getBySlugOfNonNull(slug, true);
Category category = categoryService.getBySlugOfNonNull(slug);

if (!authenticationService.categoryAuthentication(category.getId(), password)) {
throw new ForbiddenException("您没有该分类的访问权限");
Set<PostStatus> statusesToQuery = Sets.immutableEnumSet(PostStatus.PUBLISHED);
if (allowIntimatePosts(category.getId(), password)) {
statusesToQuery = Sets.immutableEnumSet(PostStatus.PUBLISHED, PostStatus.INTIMATE);
}

Page<Post> postPage =
postCategoryService.pagePostBy(category.getId(),
Sets.immutableEnumSet(PostStatus.PUBLISHED), pageable);
postCategoryService.pagePostBy(category.getId(), statusesToQuery, pageable);
return postService.convertToListVo(postPage);
}

private boolean allowIntimatePosts(Integer categoryId, String password) {
Assert.notNull(categoryId, "The categoryId must not be null.");

if (!categoryService.isPrivate(categoryId)) {
return false;
}

if (categoryAuthentication.isAuthenticated(categoryId)) {
return true;
}

if (password != null) {
ContentAuthenticationRequest authRequest =
ContentAuthenticationRequest.of(categoryId, password,
EncryptTypeEnum.CATEGORY.getName());
// authenticate this request,throw an error if authenticate failed
contentAuthenticationManager.authenticate(authRequest);
return true;
}
throw new ForbiddenException("您没有该分类的访问权限");
}
}
Loading