|
1 | 1 | defmodule EpochtalkServer.Models.Board do |
2 | 2 | use Ecto.Schema |
3 | 3 | import Ecto.Changeset |
| 4 | + import Ecto.Query |
4 | 5 | alias EpochtalkServer.Repo |
5 | 6 | alias EpochtalkServer.Models.Category |
6 | 7 | alias EpochtalkServer.Models.BoardMapping |
@@ -121,4 +122,112 @@ defmodule EpochtalkServer.Models.Board do |
121 | 122 | {:error, cs} |
122 | 123 | end |
123 | 124 | end |
| 125 | + |
| 126 | + @doc """ |
| 127 | + Determines if the provided `user_priority` has write access to the board with the specified `id` |
| 128 | +
|
| 129 | + TODO(akinsey): Should this check against banned user_priority? |
| 130 | + """ |
| 131 | + @spec get_write_access_by_id(id :: non_neg_integer, user_priority :: non_neg_integer) :: |
| 132 | + {:ok, can_write :: boolean} | {:error, :board_does_not_exist} |
| 133 | + def get_write_access_by_id(id, user_priority) do |
| 134 | + query = |
| 135 | + from b in Board, |
| 136 | + where: b.id == ^id, |
| 137 | + select: %{postable_by: b.postable_by} |
| 138 | + |
| 139 | + board = Repo.one(query) |
| 140 | + |
| 141 | + if board do |
| 142 | + # allow write if postable_by is nil |
| 143 | + can_write = is_nil(board.postable_by) |
| 144 | + # if postable_by is an integer, check against user priority |
| 145 | + can_write = |
| 146 | + if is_integer(board.postable_by), |
| 147 | + do: user_priority <= board.postable_by, |
| 148 | + else: can_write |
| 149 | + |
| 150 | + {:ok, can_write} |
| 151 | + else |
| 152 | + {:error, :board_does_not_exist} |
| 153 | + end |
| 154 | + end |
| 155 | + |
| 156 | + @doc """ |
| 157 | + Determines if the provided `user_priority` has read access to the `Board` with the specified `id`. |
| 158 | + If the user doesn't have read access to the parent of the specified `Board`, the user does not have |
| 159 | + read access to the `Board` either. |
| 160 | +
|
| 161 | + TODO(akinsey): Should this check against banned user_priority? |
| 162 | + """ |
| 163 | + @spec get_read_access_by_id(id :: non_neg_integer, user_priority :: non_neg_integer) :: |
| 164 | + {:ok, can_read :: boolean} | {:error, :board_does_not_exist} |
| 165 | + def get_read_access_by_id(id, user_priority) do |
| 166 | + find_parent_initial_query = |
| 167 | + BoardMapping |
| 168 | + |> where([bm], bm.board_id == ^id) |
| 169 | + |> select([bm], %{ |
| 170 | + board_id: bm.board_id, |
| 171 | + parent_id: bm.parent_id, |
| 172 | + category_id: bm.category_id |
| 173 | + }) |
| 174 | + |
| 175 | + find_parent_recursion_query = |
| 176 | + BoardMapping |
| 177 | + |> join(:inner, [bm], fp in "find_parent", on: bm.board_id == fp.parent_id) |
| 178 | + |> select([bm], %{ |
| 179 | + board_id: bm.board_id, |
| 180 | + parent_id: bm.parent_id, |
| 181 | + category_id: bm.category_id |
| 182 | + }) |
| 183 | + |
| 184 | + find_parent_query = |
| 185 | + find_parent_initial_query |
| 186 | + |> union(^find_parent_recursion_query) |
| 187 | + |
| 188 | + board_and_parents = |
| 189 | + Board |
| 190 | + |> recursive_ctes(true) |
| 191 | + |> with_cte("find_parent", as: ^find_parent_query) |
| 192 | + |> join(:inner, [b], fp in "find_parent", on: b.id == fp.board_id) |
| 193 | + |> join(:left, [b, fp], c in Category, on: c.id == fp.category_id) |
| 194 | + |> select([b, fp, c], %{ |
| 195 | + board_id: fp.board_id, |
| 196 | + parent_id: fp.parent_id, |
| 197 | + category_id: fp.category_id, |
| 198 | + cat_viewable_by: c.viewable_by, |
| 199 | + board_viewable_by: b.viewable_by |
| 200 | + }) |
| 201 | + |> Repo.all() |
| 202 | + |
| 203 | + # not readable if nothing in list, readable if viewable_by is nil, otherwise check viewable_by against user priority |
| 204 | + can_read = |
| 205 | + Enum.reduce(board_and_parents, length(board_and_parents) > 0, fn i, acc -> |
| 206 | + boards_viewable = |
| 207 | + not (is_integer(i.board_viewable_by) && user_priority > i.board_viewable_by) |
| 208 | + |
| 209 | + cats_viewable = not (is_integer(i.cat_viewable_by) && user_priority > i.cat_viewable_by) |
| 210 | + acc && boards_viewable && cats_viewable |
| 211 | + end) |
| 212 | + |
| 213 | + {:ok, can_read} |
| 214 | + end |
| 215 | + |
| 216 | + @doc """ |
| 217 | + Converts a board's `slug` to `id` |
| 218 | + """ |
| 219 | + @spec slug_to_id(slug :: String.t()) :: |
| 220 | + {:ok, id :: non_neg_integer} | {:error, :board_does_not_exist} |
| 221 | + def slug_to_id(slug) when is_binary(slug) do |
| 222 | + query = |
| 223 | + from b in Board, |
| 224 | + where: b.slug == ^slug, |
| 225 | + select: b.id |
| 226 | + |
| 227 | + id = Repo.one(query) |
| 228 | + |
| 229 | + if id, |
| 230 | + do: {:ok, id}, |
| 231 | + else: {:error, :board_does_not_exist} |
| 232 | + end |
124 | 233 | end |
0 commit comments