Merge pull request 'remove comment about old openssl versions in nginx config' (...
authorfloatingghost <hannah@coffee-and-dreams.uk>
Fri, 30 Dec 2022 02:53:48 +0000 (02:53 +0000)
committerfloatingghost <hannah@coffee-and-dreams.uk>
Fri, 30 Dec 2022 02:53:48 +0000 (02:53 +0000)
Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/395

38 files changed:
.gitattributes
CHANGELOG.md
docs/docs/administration/CLI_tasks/config.md
docs/docs/administration/CLI_tasks/frontend.md
docs/docs/administration/updating.md
docs/docs/installation/alpine_linux_en.md
docs/docs/installation/arch_linux_en.md
docs/docs/installation/debian_based_en.md
docs/docs/installation/fedora_based_en.md
docs/docs/installation/otp_redhat_en.md
docs/docs/installation/verifying_otp_releases.md
lib/mix/tasks/pleroma/config.ex
lib/pleroma/application.ex
lib/pleroma/password.ex [new file with mode: 0644]
lib/pleroma/user.ex
lib/pleroma/web/auth/pleroma_authenticator.ex
lib/pleroma/web/auth/totp_authenticator.ex
lib/pleroma/web/common_api/utils.ex
lib/pleroma/web/mongoose_im/mongoose_im_controller.ex
lib/pleroma/web/plugs/authentication_plug.ex
lib/pleroma/web/rel_me.ex
mix.exs
mix.lock
test/pleroma/mfa_test.exs
test/pleroma/password_test.exs [new file with mode: 0644]
test/pleroma/web/auth/basic_auth_test.exs
test/pleroma/web/auth/pleroma_authenticator_test.exs
test/pleroma/web/auth/totp_authenticator_test.exs
test/pleroma/web/mastodon_api/update_credentials_test.exs
test/pleroma/web/mongoose_im_controller_test.exs
test/pleroma/web/o_auth/ldap_authorization_test.exs
test/pleroma/web/o_auth/mfa_controller_test.exs
test/pleroma/web/o_auth/o_auth_controller_test.exs
test/pleroma/web/plugs/authentication_plug_test.exs
test/pleroma/web/twitter_api/password_controller_test.exs
test/pleroma/web/twitter_api/util_controller_test.exs
test/support/builders/user_builder.ex
test/support/factory.ex

index ac67c53c24f55f13fe5dd141589f091d6d5a5bb9..febafe62fc45e4bc85c925436dff431e1fdfb6b7 100644 (file)
@@ -1,10 +1,4 @@
 *.ex diff=elixir
 *.exs diff=elixir
 
-# Most of js/css files included in the repo are minified bundles,
-#   and we don't want to search/diff those as text files.
-*.js binary
-*.js.map binary
-*.css binary
-
 *.css diff=css
index d556b39c38bf467e92f2d542a0550d2994b065df..3106854bafb378ad96a0ac4b2b1b9abd81be8dfb 100644 (file)
@@ -10,11 +10,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Prometheus metrics exporting from `/api/v1/akkoma/metrics`
 - Ability to alter http pool size
 - Translation of statuses via ArgosTranslate
+- Argon2 password hashing
+- Ability to "verify" links in profile fields via rel=me
+- Mix tasks to dump/load config to/from json for bulk editing
 
 ### Removed
 - Non-finch HTTP adapters
 - Legacy redirect from /api/pleroma/admin to /api/v1/pleroma/admin
 - Legacy redirects from /api/pleroma to /api/v1/pleroma
+- :crypt dependency
 
 ### Changed
 - Return HTTP error 413 when uploading an avatar or banner that's above the configured upload limit instead of a 500.
index a0199d06fe6a0ab372b86eec4a672db98ac12173..31e5af401ccd8b75a88b1a9261ca05d3c7e72427 100644 (file)
@@ -155,3 +155,51 @@ This forcibly removes all saved values in the database.
     ```sh
     mix pleroma.config [--force] reset
     ```
+
+## Dumping specific configuration values to JSON
+
+If you want to bulk-modify configuration values (for example, for MRF modifications),
+it may be easier to dump the values to JSON and then modify them in a text editor.
+
+=== "OTP"
+
+    ```sh
+    ./bin/pleroma_ctl config dump_to_file group key path
+    # For example, to dump the MRF simple configuration:
+    ./bin/pleroma_ctl config dump_to_file pleroma mrf_simple /tmp/mrf_simple.json
+    ```
+
+=== "From Source"
+
+    ```sh
+    mix pleroma.config dump_to_file group key path 
+    # For example, to dump the MRF simple configuration:
+    mix pleroma.config dump_to_file pleroma mrf_simple /tmp/mrf_simple.json
+    ```
+
+## Loading specific configuration values from JSON
+
+**Note:** This will overwrite any existing value in the database, and can
+cause crashes if you do not have exactly the correct formatting.
+
+Once you have modified the JSON file, you can load it back into the database.
+
+=== "OTP"
+
+    ```sh
+    ./bin/pleroma_ctl config load_from_file path
+    # For example, to load the MRF simple configuration:
+    ./bin/pleroma_ctl config load_from_file /tmp/mrf_simple.json
+    ```
+
+=== "From Source"
+
+    ```sh
+    mix pleroma.config load_from_file path
+    # For example, to load the MRF simple configuration:
+    mix pleroma.config load_from_file /tmp/mrf_simple.json
+    ```
+
+**NOTE** an instance reboot is needed for many changes to take effect,
+you may want to visit `/api/v1/pleroma/admin/restart` on your instance
+to soft-restart the instance.
index 5e87f1227089e95a617bed8b9a9a74efb889b447..382ac268ebc5d2b36687391e6d2acd9c258a310b 100644 (file)
@@ -24,20 +24,20 @@ Currently, known `<frontend>` values are:
 
 You can still install frontends that are not configured, see below.
 
-## Example installations for a known frontend
+## Example installations for a known frontend (Stable-Version)
 
 For a frontend configured under the `available` key, it's enough to install it by name.
 
 === "OTP"
 
     ```sh
-    ./bin/pleroma_ctl frontend install pleroma-fe
+    ./bin/pleroma_ctl frontend install pleroma-fe --ref stable
     ```
 
 === "From Source"
 
     ```sh
-    mix pleroma.frontend install pleroma-fe
+    mix pleroma.frontend install pleroma-fe --ref stable
     ```
 
 This will download the latest build for the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`).
index 52979a1f5ea447bb2ff3a61433db5cb8895030a9..d0c955e1a311681845a618ece9b7691453c6df8c 100644 (file)
@@ -1,6 +1,6 @@
 # Updating your instance
 
-You should **always check the [release notes/changelog](https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/CHANGELOG.md)** in case there are config deprecations, special update steps, etc.
+You should **always check the [release notes/changelog](https://akkoma.dev/AkkomaGang/akkoma/src/branch/stable/CHANGELOG.md)** in case there are config deprecations, special update steps, etc.
 
 Besides that, doing the following is generally enough:
 ## Switch to the akkoma user
@@ -41,8 +41,10 @@ you _may_ need to specify `--flavour`, in the same way as
 Run as the `akkoma` user:
 
 ```sh
-# Pull in new changes
-git pull
+# fetch changes
+git fetch
+# check out the latest tag
+git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)
 
 # Run with production configuration
 export MIX_ENV=prod
@@ -57,7 +59,7 @@ sudo systemctl stop akkoma
 # Run database migrations
 mix ecto.migrate
 
-# Update frontend(s). See Frontend Configration doc for more information.
+# Update Pleroma-FE frontend to latest stable. For other Frontends see Frontend Configration doc for more information.
 mix pleroma.frontend install pleroma-fe --ref stable
 
 # Start akkoma (replace with your system service manager's equivalent if different)
index aae8f9626086209304a5d95c236e419b9234e6da..bdfb96d77e97b794ffd6d941132d993f490be58a 100644 (file)
@@ -84,12 +84,12 @@ doas adduser -S -s /bin/false -h /opt/akkoma -H -G akkoma akkoma
 
 **Note**: To execute a single command as the Akkoma system user, use `doas -u akkoma command`. You can also switch to a shell by using `doas -su akkoma`. If you don’t have and want `doas` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l akkoma -s $SHELL -c 'command'` and `su -l akkoma -s $SHELL` for starting a shell.
 
-* Git clone the AkkomaBE repository and make the Akkoma user the owner of the directory:
+* Git clone the AkkomaBE repository from stable-branch and make the Akkoma user the owner of the directory:
 
 ```shell
 doas mkdir -p /opt/akkoma
 doas chown -R akkoma:akkoma /opt/akkoma
-doas -u akkoma git clone https://akkoma.dev/AkkomaGang/akkoma.git /opt/akkoma
+doas -u akkoma git clone https://akkoma.dev/AkkomaGang/akkoma.git -b stable /opt/akkoma
 ```
 
 * Change to the new directory:
@@ -109,7 +109,7 @@ doas -u akkoma mix deps.get
   * This may take some time, because parts of akkoma get compiled first.
   * After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
 
-* Check the configuration and if all looks right, rename it, so Akkoma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
+* Check the configuration and if all looks right, rename it, so Akkoma will load it (`prod.secret.exs` for productive instances):
 
 ```shell
 doas -u akkoma mv config/{generated_config.exs,prod.secret.exs}
index 639c9c7986e9910a62ffb17fa66feea8f586a20b..300a5d80febc9900de359696f70048306b22885d 100644 (file)
@@ -75,12 +75,12 @@ sudo useradd -r -s /bin/false -m -d /var/lib/akkoma -U akkoma
 
 **Note**: To execute a single command as the Akkoma system user, use `sudo -Hu akkoma command`. You can also switch to a shell by using `sudo -Hu akkoma $SHELL`. If you don’t have and want `sudo` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l akkoma -s $SHELL -c 'command'` and `su -l akkoma -s $SHELL` for starting a shell.
 
-* Git clone the AkkomaBE repository and make the Akkoma user the owner of the directory:
+* Git clone the AkkomaBE repository from stable-branch and make the Akkoma user the owner of the directory:
 
 ```shell
 sudo mkdir -p /opt/akkoma
 sudo chown -R akkoma:akkoma /opt/akkoma
-sudo -Hu akkoma git clone https://akkoma.dev/AkkomaGang/akkoma.git /opt/akkoma
+sudo -Hu akkoma git clone https://akkoma.dev/AkkomaGang/akkoma.git -b stable /opt/akkoma
 ```
 
 * Change to the new directory:
@@ -100,7 +100,7 @@ sudo -Hu akkoma mix deps.get
   * This may take some time, because parts of akkoma get compiled first.
   * After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
 
-* Check the configuration and if all looks right, rename it, so Akkoma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
+* Check the configuration and if all looks right, rename it, so Akkoma will load it (`prod.secret.exs` for productive instances):
 
 ```shell
 sudo -Hu akkoma mv config/{generated_config.exs,prod.secret.exs}
index 139c789bc7767495d7f13271607887bff46bb7b3..265658fef3f849706083ec96a3f9402d5c3cfbf0 100644 (file)
@@ -49,12 +49,12 @@ sudo useradd -r -s /bin/false -m -d /var/lib/akkoma -U akkoma
 
 **Note**: To execute a single command as the Akkoma system user, use `sudo -Hu akkoma command`. You can also switch to a shell by using `sudo -Hu akkoma $SHELL`. If you don’t have and want `sudo` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l akkoma -s $SHELL -c 'command'` and `su -l akkoma -s $SHELL` for starting a shell.
 
-* Git clone the AkkomaBE repository and make the Akkoma user the owner of the directory:
+* Git clone the AkkomaBE repository from stable-branch and make the Akkoma user the owner of the directory:
 
 ```shell
 sudo mkdir -p /opt/akkoma
 sudo chown -R akkoma:akkoma /opt/akkoma
-sudo -Hu akkoma git clone https://akkoma.dev/AkkomaGang/akkoma.git /opt/akkoma
+sudo -Hu akkoma git clone https://akkoma.dev/AkkomaGang/akkoma.git -b stable /opt/akkoma
 ```
 
 * Change to the new directory:
@@ -74,7 +74,7 @@ sudo -Hu akkoma mix deps.get
   * This may take some time, because parts of akkoma get compiled first.
   * After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
 
-* Check the configuration and if all looks right, rename it, so Akkoma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
+* Check the configuration and if all looks right, rename it, so Akkoma will load it (`prod.secret.exs` for productive instances):
 
 ```shell
 sudo -Hu akkoma mv config/{generated_config.exs,prod.secret.exs}
index d8c7b3e743b4b795eb2cb35e16de847527caedce..3e09f6996b71bfab5a8bfddab1c51513b5fcd701 100644 (file)
@@ -30,11 +30,10 @@ sudo dnf install git gcc g++ make cmake file-devel postgresql-server postgresql-
 
 * Enable and initialize Postgres:
 ```shell
-sudo systemctl enable postgresql.service
 sudo postgresql-setup --initdb --unit postgresql
 # Allow password auth for postgres
 sudo sed -E -i 's|(host +all +all +127.0.0.1/32 +)ident|\1md5|' /var/lib/pgsql/data/pg_hba.conf
-sudo systemctl start postgresql.service
+sudo systemctl enable --now postgresql.service
 ```
 
 ### Install Elixir and Erlang
@@ -59,7 +58,7 @@ sudo dnf install ffmpeg
 * Install ImageMagick and ExifTool for image manipulation:
 
 ```shell
-sudo dnf install Imagemagick perl-Image-ExifTool
+sudo dnf install ImageMagick perl-Image-ExifTool
 ```
 
 
@@ -74,12 +73,12 @@ sudo useradd -r -s /bin/false -m -d /var/lib/akkoma -U akkoma
 
 **Note**: To execute a single command as the Akkoma system user, use `sudo -Hu akkoma command`. You can also switch to a shell by using `sudo -Hu akkoma $SHELL`. If you don’t have and want `sudo` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l akkoma -s $SHELL -c 'command'` and `su -l akkoma -s $SHELL` for starting a shell.
 
-* Git clone the AkkomaBE repository and make the Akkoma user the owner of the directory:
+* Git clone the AkkomaBE repository from stable-branch and make the Akkoma user the owner of the directory:
 
 ```shell
 sudo mkdir -p /opt/akkoma
 sudo chown -R akkoma:akkoma /opt/akkoma
-sudo -Hu akkoma git clone https://akkoma.dev/AkkomaGang/akkoma.git /opt/akkoma
+sudo -Hu akkoma git clone https://akkoma.dev/AkkomaGang/akkoma.git -b stable /opt/akkoma
 ```
 
 * Change to the new directory:
@@ -99,7 +98,7 @@ sudo -Hu akkoma mix deps.get
   * This may take some time, because parts of akkoma get compiled first.
   * After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
 
-* Check the configuration and if all looks right, rename it, so Akkoma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
+* Check the configuration and if all looks right, rename it, so Akkoma will load it (`prod.secret.exs` for productive instances):
 
 ```shell
 sudo -Hu akkoma mv config/{generated_config.exs,prod.secret.exs}
index ec6c30bcf99f224aabb9c8da8b099724a0813b15..1490d31391e451962ac5c1dfd6da3053ae9665e4 100644 (file)
@@ -37,7 +37,7 @@ sudo dnf install git gcc g++ erlang elixir erlang-os_mon erlang-eldap erlang-xme
 
 ```shell
 cd ~
-git clone https://akkoma.dev/AkkomaGang/akkoma.git
+git clone https://akkoma.dev/AkkomaGang/akkoma.git -b stable
 ```
 
 * Change to the new directory:
index 5f1ac69492481cea5d401276f1f73ea360398236..6e3c6f8ca209927182155e0411ffb7ebce15a30a 100644 (file)
@@ -12,7 +12,7 @@ Release URLs will always be of the form
 https://akkoma-updates.s3-website.fr-par.scw.cloud/{branch}/akkoma-{flavour}.zip
 ```
 
-Where branch is usually `stable` or `develop`, and `flavour` is
+Where branch is usually `stable` and `flavour` is
 the one [that you detect on install](../otp_en/#detecting-flavour).
 
 So, for an AMD64 stable install, your update URL will be
index c259a6cbd27ecc92c42ce01d22b86530f1282117..8661d8d7c312e2bfff69cf5a6353756bd0904268 100644 (file)
@@ -79,6 +79,45 @@ defmodule Mix.Tasks.Pleroma.Config do
     end)
   end
 
+  def run(["dump_to_file", group, key, fname]) do
+    check_configdb(fn ->
+      start_pleroma()
+
+      group = maybe_atomize(group)
+      key = maybe_atomize(key)
+
+      config = ConfigDB.get_by_group_and_key(group, key)
+
+      json =
+        %{
+          group: ConfigDB.to_json_types(config.group),
+          key: ConfigDB.to_json_types(config.key),
+          value: ConfigDB.to_json_types(config.value)
+        }
+        |> Jason.encode!()
+        |> Jason.Formatter.pretty_print()
+
+      File.write(fname, json)
+      shell_info("Wrote #{group}_#{key}.json")
+    end)
+  end
+
+  def run(["load_from_file", fname]) do
+    check_configdb(fn ->
+      start_pleroma()
+
+      json = File.read!(fname)
+      config = Jason.decode!(json)
+      group = ConfigDB.to_elixir_types(config["group"])
+      key = ConfigDB.to_elixir_types(config["key"])
+      value = ConfigDB.to_elixir_types(config["value"])
+      params = %{group: group, key: key, value: value}
+
+      ConfigDB.update_or_create(params)
+      shell_info("Loaded #{config["group"]}, #{config["key"]}")
+    end)
+  end
+
   def run(["groups"]) do
     check_configdb(fn ->
       start_pleroma()
index 26b500dc832eccd63241fcf5981b2aa84915f869..0273972bed96c425c75a15dd1d461a52cb285057 100644 (file)
@@ -159,7 +159,8 @@ defmodule Pleroma.Application do
       build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000),
       build_cachex("translations", default_ttl: :timer.hours(24 * 30), limit: 2500),
       build_cachex("instances", default_ttl: :timer.hours(24), ttl_interval: 1000, limit: 2500),
-      build_cachex("request_signatures", default_ttl: :timer.hours(24 * 30), limit: 3000)
+      build_cachex("request_signatures", default_ttl: :timer.hours(24 * 30), limit: 3000),
+      build_cachex("rel_me", default_ttl: :timer.hours(24 * 30), limit: 300)
     ]
   end
 
diff --git a/lib/pleroma/password.ex b/lib/pleroma/password.ex
new file mode 100644 (file)
index 0000000..f8cba61
--- /dev/null
@@ -0,0 +1,54 @@
+defmodule Pleroma.Password do
+  @moduledoc """
+  This module handles password hashing and verification.
+  It will delegate to the appropriate module based on the password hash.
+  It also handles upgrading of password hashes.
+  """
+
+  alias Pleroma.User
+  alias Pleroma.Password.Pbkdf2
+  require Logger
+
+  @hashing_module Argon2
+
+  @spec hash_pwd_salt(String.t()) :: String.t()
+  defdelegate hash_pwd_salt(pass), to: @hashing_module
+
+  @spec checkpw(String.t(), String.t()) :: boolean()
+  def checkpw(password, "$2" <> _ = password_hash) do
+    # Handle bcrypt passwords for Mastodon migration
+    Bcrypt.verify_pass(password, password_hash)
+  end
+
+  def checkpw(password, "$pbkdf2" <> _ = password_hash) do
+    Pbkdf2.verify_pass(password, password_hash)
+  end
+
+  def checkpw(password, "$argon2" <> _ = password_hash) do
+    Argon2.verify_pass(password, password_hash)
+  end
+
+  def checkpw(_password, _password_hash) do
+    Logger.error("Password hash not recognized")
+    false
+  end
+
+  @spec maybe_update_password(User.t(), String.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
+  def maybe_update_password(%User{password_hash: "$2" <> _} = user, password) do
+    do_update_password(user, password)
+  end
+
+  def maybe_update_password(%User{password_hash: "$6" <> _} = user, password) do
+    do_update_password(user, password)
+  end
+
+  def maybe_update_password(%User{password_hash: "$pbkdf2" <> _} = user, password) do
+    do_update_password(user, password)
+  end
+
+  def maybe_update_password(user, _), do: {:ok, user}
+
+  defp do_update_password(user, password) do
+    User.reset_password(user, %{password: password, password_confirmation: password})
+  end
+end
index d7c1511ce0acb98ea2d6610ca629785915d2185d..1ddbd36a828fbbe935272e700e213ea68100fa28 100644 (file)
@@ -479,7 +479,7 @@ defmodule Pleroma.User do
     |> validate_format(:nickname, @email_regex)
     |> validate_length(:bio, max: bio_limit)
     |> validate_length(:name, max: name_limit)
-    |> validate_fields(true)
+    |> validate_fields(true, struct)
     |> validate_non_local()
   end
 
@@ -549,7 +549,7 @@ defmodule Pleroma.User do
       :pleroma_settings_store,
       &{:ok, Map.merge(struct.pleroma_settings_store, &1)}
     )
-    |> validate_fields(false)
+    |> validate_fields(false, struct)
   end
 
   defp put_fields(changeset) do
@@ -2277,7 +2277,7 @@ defmodule Pleroma.User do
   defp put_password_hash(
          %Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset
        ) do
-    change(changeset, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+    change(changeset, password_hash: Pleroma.Password.hash_pwd_salt(password))
   end
 
   defp put_password_hash(changeset), do: changeset
@@ -2359,7 +2359,8 @@ defmodule Pleroma.User do
     |> update_and_set_cache()
   end
 
-  def validate_fields(changeset, remote? \\ false) do
+  @spec validate_fields(Ecto.Changeset.t(), Boolean.t(), User.t()) :: Ecto.Changeset.t()
+  def validate_fields(changeset, remote? \\ false, struct) do
     limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
     limit = Config.get([:instance, limit_name], 0)
 
@@ -2372,6 +2373,7 @@ defmodule Pleroma.User do
         [fields: "invalid"]
       end
     end)
+    |> maybe_validate_rel_me_field(struct)
   end
 
   defp valid_field?(%{"name" => name, "value" => value}) do
@@ -2384,6 +2386,75 @@ defmodule Pleroma.User do
 
   defp valid_field?(_), do: false
 
+  defp is_url(nil), do: nil
+
+  defp is_url(uri) do
+    case URI.parse(uri) do
+      %URI{host: nil} -> false
+      %URI{scheme: nil} -> false
+      _ -> true
+    end
+  end
+
+  @spec maybe_validate_rel_me_field(Changeset.t(), User.t()) :: Changeset.t()
+  defp maybe_validate_rel_me_field(changeset, %User{ap_id: _ap_id} = struct) do
+    fields = get_change(changeset, :fields)
+    raw_fields = get_change(changeset, :raw_fields)
+
+    if is_nil(fields) do
+      changeset
+    else
+      validate_rel_me_field(changeset, fields, raw_fields, struct)
+    end
+  end
+
+  defp maybe_validate_rel_me_field(changeset, _), do: changeset
+
+  @spec validate_rel_me_field(Changeset.t(), [Map.t()], [Map.t()], User.t()) :: Changeset.t()
+  defp validate_rel_me_field(changeset, fields, raw_fields, %User{
+         nickname: nickname,
+         ap_id: ap_id
+       }) do
+    fields =
+      fields
+      |> Enum.with_index()
+      |> Enum.map(fn {%{"name" => name, "value" => value}, index} ->
+        raw_value =
+          if is_nil(raw_fields) do
+            nil
+          else
+            Enum.at(raw_fields, index)["value"]
+          end
+
+        if is_url(raw_value) do
+          frontend_url =
+            Pleroma.Web.Router.Helpers.redirect_url(
+              Pleroma.Web.Endpoint,
+              :redirector_with_meta,
+              nickname
+            )
+
+          possible_urls = [ap_id, frontend_url]
+
+          with "me" <- RelMe.maybe_put_rel_me(raw_value, possible_urls) do
+            %{
+              "name" => name,
+              "value" => value,
+              "verified_at" => DateTime.to_iso8601(DateTime.utc_now())
+            }
+          else
+            e ->
+              Logger.error("Could not check for rel=me, #{inspect(e)}")
+              %{"name" => name, "value" => value}
+          end
+        else
+          %{"name" => name, "value" => value}
+        end
+      end)
+
+    put_change(changeset, :fields, fields)
+  end
+
   defp truncate_field(%{"name" => name, "value" => value}) do
     {name, _chopped} =
       String.split_at(name, Config.get([:instance, :account_field_name_length], 255))
@@ -2551,11 +2622,8 @@ defmodule Pleroma.User do
   # - display name
   def sanitize_html(%User{} = user, filter) do
     fields =
-      Enum.map(user.fields, fn %{"name" => name, "value" => value} ->
-        %{
-          "name" => name,
-          "value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
-        }
+      Enum.map(user.fields, fn %{"value" => value} = field ->
+        Map.put(field, "value", HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly))
       end)
 
     user
index bb377d68666ad9d317512974bcbf2e85c7a2018d..01b54037c36138efee6039c37166c4bcd1c65c73 100644 (file)
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
   alias Pleroma.Registration
   alias Pleroma.Repo
   alias Pleroma.User
-  alias Pleroma.Web.Plugs.AuthenticationPlug
 
   import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
 
@@ -15,8 +14,8 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
   def get_user(%Plug.Conn{} = conn) do
     with {:ok, {name, password}} <- fetch_credentials(conn),
          {_, %User{} = user} <- {:user, fetch_user(name)},
-         {_, true} <- {:checkpw, AuthenticationPlug.checkpw(password, user.password_hash)},
-         {:ok, user} <- AuthenticationPlug.maybe_update_password(user, password) do
+         {_, true} <- {:checkpw, Pleroma.Password.checkpw(password, user.password_hash)},
+         {:ok, user} <- Pleroma.Password.maybe_update_password(user, password) do
       {:ok, user}
     else
       {:error, _reason} = error -> error
index 5947cd8c98e622a32bfe9931b6c733b38065a2f2..e6f839e6ea6e2be1462764602945acda7eac8210 100644 (file)
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.Auth.TOTPAuthenticator do
   alias Pleroma.MFA
   alias Pleroma.MFA.TOTP
   alias Pleroma.User
-  alias Pleroma.Web.Plugs.AuthenticationPlug
 
   @doc "Verify code or check backup code."
   @spec verify(String.t(), User.t()) ::
@@ -31,7 +30,7 @@ defmodule Pleroma.Web.Auth.TOTPAuthenticator do
         code
       )
       when is_list(codes) and is_binary(code) do
-    hash_code = Enum.find(codes, fn hash -> AuthenticationPlug.checkpw(code, hash) end)
+    hash_code = Enum.find(codes, fn hash -> Pleroma.Password.checkpw(code, hash) end)
 
     if hash_code do
       MFA.invalidate_backup_code(user, hash_code)
index bf03b0a8253fec9cf6b47b27df7a9dddd316ab4a..22594be4605f24fb81b3166f1b002e9aa1df4c15 100644 (file)
@@ -17,7 +17,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
   alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.CommonAPI.ActivityDraft
   alias Pleroma.Web.MediaProxy
-  alias Pleroma.Web.Plugs.AuthenticationPlug
   alias Pleroma.Web.Utils.Params
 
   require Logger
@@ -356,7 +355,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
   @spec confirm_current_password(User.t(), String.t()) :: {:ok, User.t()} | {:error, String.t()}
   def confirm_current_password(user, password) do
     with %User{local: true} = db_user <- User.get_cached_by_id(user.id),
-         true <- AuthenticationPlug.checkpw(password, db_user.password_hash) do
+         true <- Pleroma.Password.checkpw(password, db_user.password_hash) do
       {:ok, db_user}
     else
       _ -> {:error, dgettext("errors", "Invalid password.")}
index 6ace3e0b5cac0124c29d2437357bf6cba413dd2f..85b75190b3c190adef143f990605df1dcfb54cd1 100644 (file)
@@ -7,7 +7,6 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do
 
   alias Pleroma.Repo
   alias Pleroma.User
-  alias Pleroma.Web.Plugs.AuthenticationPlug
   alias Pleroma.Web.Plugs.RateLimiter
 
   plug(RateLimiter, [name: :authentication] when action in [:user_exists, :check_password])
@@ -28,7 +27,7 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do
   def check_password(conn, %{"user" => username, "pass" => password}) do
     with %User{password_hash: password_hash, is_active: true} <-
            Repo.get_by(User, nickname: username, local: true),
-         true <- AuthenticationPlug.checkpw(password, password_hash) do
+         true <- Pleroma.Password.checkpw(password, password_hash) do
       conn
       |> json(true)
     else
index 8d58169cf61ebf2e15ac7bbabc6b81998822cfd3..894a1067e75c053d495eaa24f6240bc719819622 100644 (file)
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
 
   alias Pleroma.Helpers.AuthHelper
   alias Pleroma.User
+  alias Pleroma.Password
 
   import Plug.Conn
 
@@ -25,8 +26,8 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
         } = conn,
         _
       ) do
-    if checkpw(password, password_hash) do
-      {:ok, auth_user} = maybe_update_password(auth_user, password)
+    if Password.checkpw(password, password_hash) do
+      {:ok, auth_user} = Password.maybe_update_password(auth_user, password)
 
       conn
       |> assign(:user, auth_user)
@@ -38,35 +39,6 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
 
   def call(conn, _), do: conn
 
-  def checkpw(password, "$6" <> _ = password_hash) do
-    :crypt.crypt(password, password_hash) == password_hash
-  end
-
-  def checkpw(password, "$2" <> _ = password_hash) do
-    # Handle bcrypt passwords for Mastodon migration
-    Bcrypt.verify_pass(password, password_hash)
-  end
-
-  def checkpw(password, "$pbkdf2" <> _ = password_hash) do
-    Pleroma.Password.Pbkdf2.verify_pass(password, password_hash)
-  end
-
-  def checkpw(_password, _password_hash) do
-    Logger.error("Password hash not recognized")
-    false
-  end
-
-  def maybe_update_password(%User{password_hash: "$2" <> _} = user, password) do
-    do_update_password(user, password)
-  end
-
-  def maybe_update_password(%User{password_hash: "$6" <> _} = user, password) do
-    do_update_password(user, password)
-  end
-
-  def maybe_update_password(user, _), do: {:ok, user}
-
-  defp do_update_password(user, password) do
-    User.reset_password(user, %{password: password, password_confirmation: password})
-  end
+  @spec checkpw(String.t(), String.t()) :: boolean
+  defdelegate checkpw(password, hash), to: Password
 end
index 1826031dd4296676fa3fb9aff9900a7eee9c2fc3..3a1812f7a0e9451b441427420d12a63309c331e3 100644 (file)
@@ -38,12 +38,11 @@ defmodule Pleroma.Web.RelMe do
 
   def maybe_put_rel_me("http" <> _ = target_page, profile_urls) when is_list(profile_urls) do
     {:ok, rel_me_hrefs} = parse(target_page)
-
     true = Enum.any?(rel_me_hrefs, fn x -> x in profile_urls end)
 
     "me"
   rescue
-    _ -> nil
+    e -> nil
   end
 
   def maybe_put_rel_me(_, _) do
diff --git a/mix.exs b/mix.exs
index 4898591b1d79eb4256256f9ae92de4ec1d7d34a6..2a62dedeef438dbe1ece476c98ac6ccdbbacd234 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -143,9 +143,7 @@ defmodule Pleroma.Mixfile do
       {:sweet_xml, "~> 0.7.2"},
       {:earmark, "~> 1.4.15"},
       {:bbcode_pleroma, "~> 0.2.0"},
-      {:crypt,
-       git: "https://github.com/msantos/crypt.git",
-       ref: "f75cd55325e33cbea198fb41fe41871392f8fb76"},
+      {:argon2_elixir, "~> 3.0.0"},
       {:cors_plug, "~> 2.0"},
       {:web_push_encryption, "~> 0.3.1"},
       {:swoosh, "~> 1.0"},
index 4fa4c05ec999032daa5ccb4ae9204b0b63354f6c..dbe0ea5e39028fbcfce15902e2906264e6737ac2 100644 (file)
--- a/mix.lock
+++ b/mix.lock
@@ -1,4 +1,5 @@
 %{
+  "argon2_elixir": {:hex, :argon2_elixir, "3.0.0", "fd4405f593e77b525a5c667282172dd32772d7c4fa58cdecdaae79d2713b6c5f", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "8b753b270af557d51ba13fcdebc0f0ab27a2a6792df72fd5a6cf9cfaffcedc57"},
   "base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"},
   "bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"},
   "bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"},
@@ -18,7 +19,6 @@
   "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"},
   "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
   "credo": {:git, "https://github.com/rrrene/credo.git", "1c1b99ea41a457761383d81aaf6a606913996fe7", [ref: "1c1b99ea41a457761383d81aaf6a606913996fe7"]},
-  "crypt": {:git, "https://github.com/msantos/crypt.git", "f75cd55325e33cbea198fb41fe41871392f8fb76", [ref: "f75cd55325e33cbea198fb41fe41871392f8fb76"]},
   "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
   "db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
   "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
index 76ba1a99d891f3bea92a4a47609d7905169f221a..66660d97e1cc5cbf424f54b6631dc6d966d63f7d 100644 (file)
@@ -30,8 +30,8 @@ defmodule Pleroma.MFATest do
       {:ok, [code1, code2]} = MFA.generate_backup_codes(user)
       updated_user = refresh_record(user)
       [hash1, hash2] = updated_user.multi_factor_authentication_settings.backup_codes
-      assert Pleroma.Password.Pbkdf2.verify_pass(code1, hash1)
-      assert Pleroma.Password.Pbkdf2.verify_pass(code2, hash2)
+      assert Pleroma.Password.checkpw(code1, hash1)
+      assert Pleroma.Password.checkpw(code2, hash2)
     end
   end
 
diff --git a/test/pleroma/password_test.exs b/test/pleroma/password_test.exs
new file mode 100644 (file)
index 0000000..951fc81
--- /dev/null
@@ -0,0 +1,65 @@
+defmodule Pleroma.PasswordTest do
+  use Pleroma.DataCase, async: true
+  import Pleroma.Factory
+  import ExUnit.CaptureLog
+
+  alias Pleroma.Password
+
+  describe "hash_pwd_salt/1" do
+    test "returns a hash" do
+      assert "$argon2id" <> _ = Password.hash_pwd_salt("test")
+    end
+  end
+
+  describe "maybe_update_password/2" do
+    test "with a bcrypt hash, it updates to an argon2 hash" do
+      user = insert(:user, password_hash: Bcrypt.hash_pwd_salt("123"))
+      assert "$2" <> _ = user.password_hash
+
+      {:ok, user} = Password.maybe_update_password(user, "123")
+      assert "$argon2" <> _ = user.password_hash
+    end
+
+    test "with a pbkdf2 hash, it updates to an argon2 hash" do
+      user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("123"))
+      assert "$pbkdf2" <> _ = user.password_hash
+
+      {:ok, user} = Password.maybe_update_password(user, "123")
+      assert "$argon2" <> _ = user.password_hash
+    end
+  end
+
+  describe "checkpw/2" do
+    test "check pbkdf2 hash" do
+      hash =
+        "$pbkdf2-sha512$160000$loXqbp8GYls43F0i6lEfIw$AY.Ep.2pGe57j2hAPY635sI/6w7l9Q9u9Bp02PkPmF3OrClDtJAI8bCiivPr53OKMF7ph6iHhN68Rom5nEfC2A"
+
+      assert Password.checkpw("test-password", hash)
+      refute Password.checkpw("test-password1", hash)
+    end
+
+    test "check bcrypt hash" do
+      hash = "$2a$10$uyhC/R/zoE1ndwwCtMusK.TLVzkQ/Ugsbqp3uXI.CTTz0gBw.24jS"
+
+      assert Password.checkpw("password", hash)
+      refute Password.checkpw("password1", hash)
+    end
+
+    test "check argon2 hash" do
+      hash =
+        "$argon2id$v=19$m=65536,t=8,p=2$zEMMsTuK5KkL5AFWbX7jyQ$VyaQD7PF6e9btz0oH1YiAkWwIGZ7WNDZP8l+a/O171g"
+
+      assert Password.checkpw("password", hash)
+      refute Password.checkpw("password1", hash)
+    end
+
+    test "it returns false when hash invalid" do
+      hash =
+        "psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
+
+      assert capture_log(fn ->
+               refute Password.checkpw("password", hash)
+             end) =~ "[error] Password hash not recognized"
+    end
+  end
+end
index 2816aae4ceee1b167bb2e9cdd6946a44be1b7f18..a357ba4a521326eee626affcbf2737c0d9df78dc 100644 (file)
@@ -11,7 +11,7 @@ defmodule Pleroma.Web.Auth.BasicAuthTest do
     conn: conn
   } do
     user = insert(:user)
-    assert Pleroma.Password.Pbkdf2.verify_pass("test", user.password_hash)
+    assert Pleroma.Password.checkpw("test", user.password_hash)
 
     basic_auth_contents =
       (URI.encode_www_form(user.nickname) <> ":" <> URI.encode_www_form("test"))
index b1397c523096e1d45aba99c7b4f8caeabbe397d3..fb3c474172e736f164c296e47b1c2ee467dce242 100644 (file)
@@ -15,7 +15,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do
     user =
       insert(:user,
         nickname: name,
-        password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password)
+        password_hash: Pleroma.Password.hash_pwd_salt(password)
       )
 
     {:ok, [user: user, name: name, password: password]}
@@ -30,7 +30,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do
 
     assert {:ok, returned_user} = res
     assert returned_user.id == user.id
-    assert "$pbkdf2" <> _ = returned_user.password_hash
+    assert "$argon2" <> _ = returned_user.password_hash
   end
 
   test "get_user/authorization with invalid password", %{name: name} do
index ac4209f2d92f16537b57ccc1c0f47738fa1ac46f..6d2646b617d1c10c3fba38d074e98e5af2e437a0 100644 (file)
@@ -34,7 +34,7 @@ defmodule Pleroma.Web.Auth.TOTPAuthenticatorTest do
 
     hashed_codes =
       backup_codes
-      |> Enum.map(&Pleroma.Password.Pbkdf2.hash_pwd_salt(&1))
+      |> Enum.map(&Pleroma.Password.hash_pwd_salt(&1))
 
     user =
       insert(:user,
index 2ba909dadca93e2192a39ffc46ac97616eb0202c..e9b8825bfb7801f7c5507175ecb2fb9b82dbbf31 100644 (file)
@@ -465,6 +465,69 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do
              ]
     end
 
+    test "update fields with a link to content with rel=me, with ap id", %{user: user, conn: conn} do
+      Tesla.Mock.mock(fn
+        %{url: "http://example.com/rel_me/ap_id"} ->
+          %Tesla.Env{
+            status: 200,
+            body: ~s[<html><head><link rel="me" href="#{user.ap_id}"></head></html>]
+          }
+      end)
+
+      field = %{name: "Website", value: "http://example.com/rel_me/ap_id"}
+
+      account_data =
+        conn
+        |> patch("/api/v1/accounts/update_credentials", %{fields_attributes: [field]})
+        |> json_response_and_validate_schema(200)
+
+      assert [
+               %{
+                 "name" => "Website",
+                 "value" =>
+                   ~s[<a href="http://example.com/rel_me/ap_id" rel="ugc">http://example.com/rel_me/ap_id</a>],
+                 "verified_at" => verified_at
+               }
+             ] = account_data["fields"]
+
+      {:ok, verified_at, _} = DateTime.from_iso8601(verified_at)
+      assert DateTime.diff(DateTime.utc_now(), verified_at) < 10
+    end
+
+    test "update fields with a link to content with rel=me, with frontend path", %{
+      user: user,
+      conn: conn
+    } do
+      fe_url = "#{Pleroma.Web.Endpoint.url()}/#{user.nickname}"
+
+      Tesla.Mock.mock(fn
+        %{url: "http://example.com/rel_me/fe_path"} ->
+          %Tesla.Env{
+            status: 200,
+            body: ~s[<html><head><link rel="me" href="#{fe_url}"></head></html>]
+          }
+      end)
+
+      field = %{name: "Website", value: "http://example.com/rel_me/fe_path"}
+
+      account_data =
+        conn
+        |> patch("/api/v1/accounts/update_credentials", %{fields_attributes: [field]})
+        |> json_response_and_validate_schema(200)
+
+      assert [
+               %{
+                 "name" => "Website",
+                 "value" =>
+                   ~s[<a href="http://example.com/rel_me/fe_path" rel="ugc">http://example.com/rel_me/fe_path</a>],
+                 "verified_at" => verified_at
+               }
+             ] = account_data["fields"]
+
+      {:ok, verified_at, _} = DateTime.from_iso8601(verified_at)
+      assert DateTime.diff(DateTime.utc_now(), verified_at) < 10
+    end
+
     test "emojis in fields labels", %{conn: conn} do
       fields = [
         %{name: ":firefox:", value: "is best 2hu"},
index 43c4dfa33cdde5a5862e88ea4d5ad9a9bc1c65f3..73473ccf54df8f1e6059535c375042278096e380 100644 (file)
@@ -41,13 +41,13 @@ defmodule Pleroma.Web.MongooseIMControllerTest do
   end
 
   test "/check_password", %{conn: conn} do
-    user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("cool"))
+    user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt("cool"))
 
     _deactivated_user =
       insert(:user,
         nickname: "konata",
         is_active: false,
-        password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("cool")
+        password_hash: Pleroma.Password.hash_pwd_salt("cool")
       )
 
     res =
index c8a1d65ab5f0614c6174186df96a662b589aa597..502ee091876049622644be709594f0c48ec2d736 100644 (file)
@@ -18,7 +18,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
   @tag @skip
   test "authorizes the existing user using LDAP credentials" do
     password = "testpassword"
-    user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+    user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt(password))
     app = insert(:oauth_app, scopes: ["read", "write"])
 
     host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
@@ -101,7 +101,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
   @tag @skip
   test "disallow authorization for wrong LDAP credentials" do
     password = "testpassword"
-    user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+    user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt(password))
     app = insert(:oauth_app, scopes: ["read", "write"])
 
     host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
index 17bbde85b40a974ab45070afd08f6e3e2e4932f8..dacf03b2bdb9d3277a2651ec72dbb16802f65516 100644 (file)
@@ -20,7 +20,7 @@ defmodule Pleroma.Web.OAuth.MFAControllerTest do
       insert(:user,
         multi_factor_authentication_settings: %MFA.Settings{
           enabled: true,
-          backup_codes: [Pleroma.Password.Pbkdf2.hash_pwd_salt("test-code")],
+          backup_codes: [Pleroma.Password.hash_pwd_salt("test-code")],
           totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
         }
       )
@@ -246,7 +246,7 @@ defmodule Pleroma.Web.OAuth.MFAControllerTest do
 
       hashed_codes =
         backup_codes
-        |> Enum.map(&Pleroma.Password.Pbkdf2.hash_pwd_salt(&1))
+        |> Enum.map(&Pleroma.Password.hash_pwd_salt(&1))
 
       user =
         insert(:user,
index 7240624efd62710fd91832716c739b7f0fcfda68..303bc2cf2efda5dbf4db7ec3e724bf176442261f 100644 (file)
@@ -316,7 +316,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
            app: app,
            conn: conn
          } do
-      user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("testpassword"))
+      user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt("testpassword"))
       registration = insert(:registration, user: nil)
       redirect_uri = OAuthController.default_redirect_uri(app)
 
@@ -347,7 +347,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
            app: app,
            conn: conn
          } do
-      user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("testpassword"))
+      user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt("testpassword"))
       registration = insert(:registration, user: nil)
       unlisted_redirect_uri = "http://cross-site-request.com"
 
@@ -917,7 +917,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
     test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do
       password = "testpassword"
-      user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+      user = insert(:user, password_hash: Pleroma.Password.hash_pwd_salt(password))
 
       app = insert(:oauth_app, scopes: ["read", "write"])
 
@@ -947,7 +947,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
       user =
         insert(:user,
-          password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password),
+          password_hash: Pleroma.Password.hash_pwd_salt(password),
           multi_factor_authentication_settings: %MFA.Settings{
             enabled: true,
             totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
@@ -1056,7 +1056,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
       password = "testpassword"
 
       {:ok, user} =
-        insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+        insert(:user, password_hash: Pleroma.Password.hash_pwd_salt(password))
         |> User.confirmation_changeset(set_confirmation: false)
         |> User.update_and_set_cache()
 
@@ -1084,7 +1084,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
       user =
         insert(:user,
-          password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password),
+          password_hash: Pleroma.Password.hash_pwd_salt(password),
           is_active: false
         )
 
@@ -1112,7 +1112,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
       user =
         insert(:user,
-          password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password),
+          password_hash: Pleroma.Password.hash_pwd_salt(password),
           password_reset_pending: true
         )
 
@@ -1141,7 +1141,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
       user =
         insert(:user,
-          password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password),
+          password_hash: Pleroma.Password.hash_pwd_salt(password),
           is_confirmed: false
         )
 
@@ -1169,7 +1169,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
       user =
         insert(:user,
-          password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password),
+          password_hash: Pleroma.Password.hash_pwd_salt(password),
           is_approved: false
         )
 
index 118ab302a0c956ad1217f141a5c927e20cf3cf93..1fbc17a924360a0c3eea9decc484c9646ead6043 100644 (file)
@@ -17,7 +17,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
     user = %User{
       id: 1,
       name: "dude",
-      password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("guy")
+      password_hash: Pleroma.Password.hash_pwd_salt("guy")
     }
 
     conn =
@@ -52,7 +52,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
     assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
   end
 
-  test "with a bcrypt hash, it updates to a pkbdf2 hash", %{conn: conn} do
+  test "with a bcrypt hash, it updates to an argon2 hash", %{conn: conn} do
     user = insert(:user, password_hash: Bcrypt.hash_pwd_salt("123"))
     assert "$2" <> _ = user.password_hash
 
@@ -67,21 +67,17 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
     assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
 
     user = User.get_by_id(user.id)
-    assert "$pbkdf2" <> _ = user.password_hash
+    assert "$argon2" <> _ = user.password_hash
   end
 
-  @tag :skip_on_mac
-  test "with a crypt hash, it updates to a pkbdf2 hash", %{conn: conn} do
-    user =
-      insert(:user,
-        password_hash:
-          "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
-      )
+  test "with a pbkdf2 hash, it updates to an argon2 hash", %{conn: conn} do
+    user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("123"))
+    assert "$pbkdf2" <> _ = user.password_hash
 
     conn =
       conn
       |> assign(:auth_user, user)
-      |> assign(:auth_credentials, %{password: "password"})
+      |> assign(:auth_credentials, %{password: "123"})
       |> AuthenticationPlug.call(%{})
 
     assert conn.assigns.user.id == conn.assigns.auth_user.id
@@ -89,7 +85,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
     assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
 
     user = User.get_by_id(user.id)
-    assert "$pbkdf2" <> _ = user.password_hash
+    assert "$argon2" <> _ = user.password_hash
   end
 
   describe "checkpw/2" do
@@ -101,16 +97,16 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
       refute AuthenticationPlug.checkpw("test-password1", hash)
     end
 
-    @tag :skip_on_mac
-    test "check sha512-crypt hash" do
-      hash =
-        "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
+    test "check bcrypt hash" do
+      hash = "$2a$10$uyhC/R/zoE1ndwwCtMusK.TLVzkQ/Ugsbqp3uXI.CTTz0gBw.24jS"
 
       assert AuthenticationPlug.checkpw("password", hash)
+      refute AuthenticationPlug.checkpw("password1", hash)
     end
 
-    test "check bcrypt hash" do
-      hash = "$2a$10$uyhC/R/zoE1ndwwCtMusK.TLVzkQ/Ugsbqp3uXI.CTTz0gBw.24jS"
+    test "check argon2 hash" do
+      hash =
+        "$argon2id$v=19$m=65536,t=8,p=2$zEMMsTuK5KkL5AFWbX7jyQ$VyaQD7PF6e9btz0oH1YiAkWwIGZ7WNDZP8l+a/O171g"
 
       assert AuthenticationPlug.checkpw("password", hash)
       refute AuthenticationPlug.checkpw("password1", hash)
index 05c3561bf82a18bb2d50c5ce1906562d72c6eab4..4ff792dc8745db3d0339ed65dbfad7ddb969b798 100644 (file)
@@ -96,7 +96,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
       assert response =~ "<h2>Password changed!</h2>"
 
       user = refresh_record(user)
-      assert Pleroma.Password.Pbkdf2.verify_pass("test", user.password_hash)
+      assert Pleroma.Password.checkpw("test", user.password_hash)
       assert Enum.empty?(Token.get_user_tokens(user))
     end
 
index 3f839568d24c9e2a8d5edcacbb56d45672d92d56..51f216bf192c6deee8b9549745db1c70b0e2766f 100644 (file)
@@ -553,7 +553,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
 
       assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"}
       fetched_user = User.get_cached_by_id(user.id)
-      assert Pleroma.Password.Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true
+      assert Pleroma.Password.checkpw("newpass", fetched_user.password_hash) == true
     end
   end
 
index 6bccbb35aeea5485ea008f72a39364a08b7d6dfb..27470498df2e1637faa8d020583edd7887a4fb69 100644 (file)
@@ -7,7 +7,7 @@ defmodule Pleroma.Builders.UserBuilder do
       email: "test@example.org",
       name: "Test Name",
       nickname: "testname",
-      password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"),
+      password_hash: Pleroma.Password.hash_pwd_salt("test"),
       bio: "A tester.",
       ap_id: "some id",
       last_digest_emailed_at: NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
index 84e076137401a17e834eb21c39dba7e94d343c02..42c940c52603492fbdadba053622cc086ead1a21 100644 (file)
@@ -47,12 +47,16 @@ defmodule Pleroma.Factory do
 
   def user_factory(attrs \\ %{}) do
     pem = Enum.random(@rsa_keys)
+    # Argon2.hash_pwd_salt("test")
+    # it really eats CPU time, so we use a precomputed hash
+    password_hash =
+      "$argon2id$v=19$m=65536,t=8,p=2$FEAarFuiOsROO24NHIHMYw$oxdaz2fTPpuU+dYCl60FsqE65T1Tjy6lGikKfmql4xo"
 
     user = %User{
       name: sequence(:name, &"Test テスト User #{&1}"),
       email: sequence(:email, &"user#{&1}@example.com"),
       nickname: sequence(:nickname, &"nick#{&1}"),
-      password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"),
+      password_hash: password_hash,
       bio: sequence(:bio, &"Tester Number #{&1}"),
       is_discoverable: true,
       last_digest_emailed_at: NaiveDateTime.utc_now(),