diff options
Diffstat (limited to 'src/main/java')
7 files changed, 160 insertions, 37 deletions
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/accesscontrol/Role.java b/src/main/java/eu/mulk/mulkcms2/benki/accesscontrol/Role.java index 6298245..349322b 100644 --- a/src/main/java/eu/mulk/mulkcms2/benki/accesscontrol/Role.java +++ b/src/main/java/eu/mulk/mulkcms2/benki/accesscontrol/Role.java @@ -5,6 +5,7 @@ import eu.mulk.mulkcms2.benki.users.User;  import eu.mulk.mulkcms2.benki.users.UserRole;  import io.quarkus.hibernate.orm.panache.PanacheEntityBase;  import java.util.Collection; +import java.util.Objects;  import java.util.Set;  import javax.persistence.CollectionTable;  import javax.persistence.Column; @@ -78,4 +79,21 @@ public class Role extends PanacheEntityBase {    public static Role getWorld() {      return find("from Role r join r.tags tag where tag = 'world'").singleResult();    } + +  @Override +  public boolean equals(Object o) { +    if (this == o) { +      return true; +    } +    if (!(o instanceof Role)) { +      return false; +    } +    Role role = (Role) o; +    return Objects.equals(id, role.id); +  } + +  @Override +  public int hashCode() { +    return Objects.hash(id); +  }  } diff --git a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java index f2e3067..cb4c20f 100644 --- a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java +++ b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java @@ -3,10 +3,9 @@ package eu.mulk.mulkcms2.benki.bookmarks;  import static javax.ws.rs.core.MediaType.APPLICATION_JSON;  import static javax.ws.rs.core.MediaType.TEXT_HTML; -import eu.mulk.mulkcms2.benki.accesscontrol.Role; +import eu.mulk.mulkcms2.benki.posts.Post;  import eu.mulk.mulkcms2.benki.posts.PostFilter;  import eu.mulk.mulkcms2.benki.posts.PostResource; -import eu.mulk.mulkcms2.benki.users.User;  import io.quarkus.qute.Template;  import io.quarkus.qute.TemplateInstance;  import io.quarkus.qute.api.ResourcePath; @@ -21,9 +20,6 @@ import javax.inject.Inject;  import javax.json.JsonObject;  import javax.transaction.Transactional;  import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.ws.rs.BadRequestException;  import javax.ws.rs.FormParam;  import javax.ws.rs.GET;  import javax.ws.rs.POST; @@ -51,12 +47,10 @@ public class BookmarkResource extends PostResource {        @FormParam("uri") URI uri,        @FormParam("title") @NotEmpty String title,        @FormParam("description") String description, -      @FormParam("visibility") @NotNull @Pattern(regexp = "public|semiprivate|private") -          String visibility) +      @FormParam("visibility") Post.Visibility visibility)        throws URISyntaxException { -    var userName = identity.getPrincipal().getName(); -    var user = User.findByNickname(userName); +    var user = getCurrentUser();      var bookmark = new Bookmark();      bookmark.uri = uri.toString(); @@ -66,14 +60,7 @@ public class BookmarkResource extends PostResource {      bookmark.owner = user;      bookmark.date = OffsetDateTime.now(); -    if (visibility.equals("public")) { -      Role world = Role.find("from Role r join r.tags tag where tag = 'world'").singleResult(); -      bookmark.targets = Set.of(world); -    } else if (visibility.equals("semiprivate")) { -      bookmark.targets = Set.copyOf(user.defaultTargets); -    } else if (!visibility.equals("private")) { -      throw new BadRequestException(String.format("invalid visibility “%s”", visibility)); -    } +    assignPostTargets(visibility, user, bookmark);      bookmark.persistAndFlush(); diff --git a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java index 918cad7..1e92c38 100644 --- a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java +++ b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java @@ -4,6 +4,7 @@ import eu.mulk.mulkcms2.benki.posts.Post;  import eu.mulk.mulkcms2.common.markdown.MarkdownConverter;  import java.util.Collection;  import javax.annotation.CheckForNull; +import javax.json.bind.annotation.JsonbTransient;  import javax.persistence.Column;  import javax.persistence.Entity;  import javax.persistence.FetchType; @@ -22,9 +23,11 @@ public class LazychatMessage extends Post {    public String format;    @OneToMany(mappedBy = "referrer", fetch = FetchType.LAZY) +  @JsonbTransient    public Collection<LazychatReference> references;    @Transient +  @JsonbTransient    public String getContentHtml() {      return new MarkdownConverter().htmlify(content);    } @@ -43,6 +46,7 @@ public class LazychatMessage extends Post {    @CheckForNull    @Override +  @JsonbTransient    public String getDescriptionHtml() {      return getContentHtml();    } diff --git a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java index 8a4d2a3..fd672f8 100644 --- a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java +++ b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatResource.java @@ -1,21 +1,24 @@  package eu.mulk.mulkcms2.benki.lazychat; -import eu.mulk.mulkcms2.benki.accesscontrol.Role; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +import eu.mulk.mulkcms2.benki.posts.Post;  import eu.mulk.mulkcms2.benki.posts.PostFilter;  import eu.mulk.mulkcms2.benki.posts.PostResource; -import eu.mulk.mulkcms2.benki.users.User;  import io.quarkus.security.Authenticated;  import java.net.URI;  import java.net.URISyntaxException;  import java.time.OffsetDateTime; -import java.util.Set; +import java.util.Objects;  import javax.transaction.Transactional; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.ws.rs.BadRequestException; +import javax.ws.rs.ForbiddenException;  import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException;  import javax.ws.rs.POST;  import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces;  import javax.ws.rs.core.Response;  @Path("/lazychat") @@ -29,13 +32,10 @@ public class LazychatResource extends PostResource {    @Transactional    @Authenticated    public Response postMessage( -      @FormParam("text") String text, -      @FormParam("visibility") @NotNull @Pattern(regexp = "public|semiprivate|private") -          String visibility) +      @FormParam("text") String text, @FormParam("visibility") Post.Visibility visibility)        throws URISyntaxException { -    var userName = identity.getPrincipal().getName(); -    var user = User.findByNickname(userName); +    var user = getCurrentUser();      var message = new LazychatMessage();      message.content = text; @@ -43,17 +43,57 @@ public class LazychatResource extends PostResource {      message.owner = user;      message.date = OffsetDateTime.now(); -    if (visibility.equals("public")) { -      Role world = Role.find("from Role r join r.tags tag where tag = 'world'").singleResult(); -      message.targets = Set.of(world); -    } else if (visibility.equals("semiprivate")) { -      message.targets = Set.copyOf(user.defaultTargets); -    } else if (!visibility.equals("private")) { -      throw new BadRequestException(String.format("invalid visibility “%s”", visibility)); -    } +    assignPostTargets(visibility, user, message);      message.persistAndFlush();      return Response.seeOther(new URI("/lazychat")).build();    } + +  @POST +  @Transactional +  @Authenticated +  @Path("/p/{id}/edit") +  public Response patchMessage( +      @PathParam("id") int id, +      @FormParam("text") String text, +      @FormParam("visibility") Post.Visibility visibility) +      throws URISyntaxException { + +    var user = getCurrentUser(); + +    var message = getSession().byId(LazychatMessage.class).load(id); + +    if (message == null) { +      throw new NotFoundException(); +    } + +    if (!Objects.equals(message.owner.id, user.id)) { +      throw new ForbiddenException(); +    } + +    message.content = text; +    message.format = "markdown"; + +    assignPostTargets(visibility, user, message); + +    return Response.seeOther(new URI("/lazychat")).build(); +  } + +  @GET +  @Transactional +  @Produces(APPLICATION_JSON) +  @Path("/p/{id}") +  public LazychatMessage getMessage(@PathParam("id") int id) { + +    var user = getCurrentUser(); + +    var message = getSession().byId(LazychatMessage.class).load(id); + +    if (!user.canSee(message)) { +      throw new ForbiddenException(); +    } + +    return message; +  }  } diff --git a/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java b/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java index 20aec05..356461c 100644 --- a/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java +++ b/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java @@ -13,6 +13,7 @@ import java.util.List;  import java.util.Objects;  import java.util.Set;  import javax.annotation.CheckForNull; +import javax.json.bind.annotation.JsonbTransient;  import javax.persistence.Column;  import javax.persistence.Entity;  import javax.persistence.FetchType; @@ -57,6 +58,7 @@ public abstract class Post extends PanacheEntityBase {    @ManyToOne(fetch = FetchType.LAZY)    @JoinColumn(name = "owner", referencedColumnName = "id") +  @JsonbTransient    public User owner;    @ManyToMany(fetch = FetchType.LAZY) @@ -65,6 +67,7 @@ public abstract class Post extends PanacheEntityBase {        schema = "benki",        joinColumns = @JoinColumn(name = "message"),        inverseJoinColumns = @JoinColumn(name = "user")) +  @JsonbTransient    public Set<User> visibleTo;    @ManyToMany(fetch = FetchType.LAZY) @@ -73,6 +76,7 @@ public abstract class Post extends PanacheEntityBase {        schema = "benki",        joinColumns = @JoinColumn(name = "message"),        inverseJoinColumns = @JoinColumn(name = "target")) +  @JsonbTransient    public Set<Role> targets;    public abstract boolean isBookmark(); @@ -88,6 +92,18 @@ public abstract class Post extends PanacheEntityBase {    @CheckForNull    public abstract String getUri(); +  public Visibility getVisibility() { +    if (targets.isEmpty()) { +      return Visibility.PRIVATE; +    } else if (targets.contains(Role.getWorld())) { +      return Visibility.PUBLIC; +    } else { +      // FIXME: There should really be a check whether targets.equals(owner.defaultTargets) here. +      // Otherwise the actual visibility is DISCRETIONARY. +      return Visibility.SEMIPRIVATE; +    } +  } +    protected static <T extends Post> CriteriaQuery<T> queryViewable(        Class<T> entityClass,        SecurityIdentity readerIdentity, @@ -236,4 +252,28 @@ public abstract class Post extends PanacheEntityBase {      return new PostPage<T>(prevCursor, cursor, nextCursor, forwardResults);    } + +  public enum Visibility { +    PUBLIC, +    SEMIPRIVATE, +    DISCRETIONARY, +    PRIVATE, +  } + +  @Override +  public boolean equals(Object o) { +    if (this == o) { +      return true; +    } +    if (!(o instanceof Post)) { +      return false; +    } +    Post post = (Post) o; +    return Objects.equals(id, post.id); +  } + +  @Override +  public int hashCode() { +    return Objects.hash(id); +  }  } diff --git a/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java b/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java index fbe6bf7..a691490 100644 --- a/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java +++ b/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java @@ -10,6 +10,7 @@ import com.rometools.rome.feed.atom.Link;  import com.rometools.rome.feed.synd.SyndPersonImpl;  import com.rometools.rome.io.FeedException;  import com.rometools.rome.io.WireFeedOutput; +import eu.mulk.mulkcms2.benki.accesscontrol.Role;  import eu.mulk.mulkcms2.benki.users.User;  import io.quarkus.qute.Template;  import io.quarkus.qute.TemplateExtension; @@ -26,6 +27,7 @@ import java.time.temporal.TemporalAccessor;  import java.util.Comparator;  import java.util.Date;  import java.util.List; +import java.util.Set;  import java.util.stream.Collectors;  import javax.annotation.CheckForNull;  import javax.annotation.Nullable; @@ -33,6 +35,7 @@ import javax.inject.Inject;  import javax.json.spi.JsonProvider;  import javax.persistence.EntityManager;  import javax.persistence.PersistenceContext; +import javax.ws.rs.BadRequestException;  import javax.ws.rs.GET;  import javax.ws.rs.Path;  import javax.ws.rs.PathParam; @@ -70,7 +73,7 @@ public abstract class PostResource {    @ConfigProperty(name = "mulkcms.tag-base")    String tagBase; -  @PersistenceContext EntityManager entityManager; +  @PersistenceContext protected EntityManager entityManager;    private final PostFilter postFilter;    private final String pageTitle; @@ -256,4 +259,29 @@ public abstract class PostResource {          throw new IllegalStateException();      }    } + +  protected Session getSession() { +    return entityManager.unwrap(Session.class); +  } + +  protected static void assignPostTargets(Post.Visibility visibility, User user, Post post) { +    switch (visibility) { +      case PUBLIC: +        post.targets = Set.of(Role.getWorld()); +        break; +      case SEMIPRIVATE: +        post.targets = Set.copyOf(user.defaultTargets); +        break; +      case PRIVATE: +        post.targets = Set.of(); +        break; +      default: +        throw new BadRequestException(String.format("invalid visibility “%s”", visibility)); +    } +  } + +  protected User getCurrentUser() { +    var userName = identity.getPrincipal().getName(); +    return User.findByNickname(userName); +  }  } diff --git a/src/main/java/eu/mulk/mulkcms2/benki/users/User.java b/src/main/java/eu/mulk/mulkcms2/benki/users/User.java index 5879046..ab89baa 100644 --- a/src/main/java/eu/mulk/mulkcms2/benki/users/User.java +++ b/src/main/java/eu/mulk/mulkcms2/benki/users/User.java @@ -5,6 +5,7 @@ import eu.mulk.mulkcms2.benki.accesscontrol.Role;  import eu.mulk.mulkcms2.benki.bookmarks.Bookmark;  import eu.mulk.mulkcms2.benki.lazychat.LazychatMessage;  import eu.mulk.mulkcms2.benki.posts.Post; +import eu.mulk.mulkcms2.benki.posts.Post.Visibility;  import eu.mulk.mulkcms2.benki.wiki.WikiPageRevision;  import io.quarkus.hibernate.orm.panache.PanacheEntityBase;  import java.util.Collection; @@ -141,4 +142,9 @@ public class User extends PanacheEntityBase {    public static User findByNickname(String nickname) {      return User.find("from BenkiUser u join u.nicknames n where ?1 = n", nickname).singleResult();    } + +  public boolean canSee(Post message) { +    // FIXME: Make this more efficient. +    return message.getVisibility() == Visibility.PUBLIC || visiblePosts.contains(message); +  }  }  | 
