File size: 1,660 Bytes
8da2481
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
defmodule Plausible.Site.SharedLink do
  @moduledoc """
  Schema for shared links.
  """
  use Ecto.Schema
  import Ecto.Changeset

  @type t() :: %__MODULE__{}

  schema "shared_links" do
    belongs_to :site, Plausible.Site
    field :name, :string
    field :slug, :string
    field :password_hash, :string
    field :password, :string, virtual: true
    belongs_to :segment, Plausible.Segments.Segment

    timestamps()
  end

  def changeset(link, attrs \\ %{}, opts \\ []) do
    link
    |> cast(attrs, [:password, :name])
    |> validate_required([:slug, :name])
    |> validate_special_name(opts)
    |> unique_constraint(:slug)
    |> unique_constraint(:name, name: :shared_links_site_id_name_index)
    |> foreign_key_constraint(:segment_id)
    |> hash_password()
  end

  defp validate_special_name(changeset, opts) do
    name = get_change(changeset, :name)

    if name not in Plausible.Sites.shared_link_special_names() ||
         Keyword.get(opts, :skip_special_name_check?, false) do
      changeset
    else
      changeset |> add_error(:name, "This name is reserved. Please choose another one")
    end
  end

  defp hash_password(link) do
    case link.changes[:password] do
      nil ->
        link

      password ->
        hash = Plausible.Auth.Password.hash(password)
        change(link, password_hash: hash)
    end
  end

  def password_protected?(%__MODULE__{password_hash: hash}) when not is_nil(hash), do: true
  def password_protected?(%__MODULE__{}), do: false

  def limited_to_segment?(%__MODULE__{segment_id: segment_id}) when is_integer(segment_id),
    do: true

  def limited_to_segment?(%__MODULE__{}), do: false
end