diff --git a/.travis.yml b/.travis.yml index c8cdea00..9974037c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,11 +6,11 @@ services: jobs: include: - stage: test - elixir: 1.6 + elixir: 1.7 otp_release: 20.0 script: &test_scripts - mix test - - MIX_ENV=test mix credo --ignore design.tagtodo + - MIX_ENV=test mix credo - stage: test elixir: 1.8 script: *test_scripts diff --git a/CHANGELOG.md b/CHANGELOG.md index c617704a..8f300b16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,355 +1,58 @@ # Changelog -## v1.0.19 (TBA) - -**Warning:** This release will now sign and verify all tokens, causing previous tokens to no longer work. Any sessions and persistent sessions will be invalidated. - -### Enhancements - -* [`Pow.Plug.Session`] Now sets a global lock when renewing the session -* [`PowPersistentSession.Plug.Cookie`] Now sets a global lock when authenticating the user -* [`PowEmailConfirmation.Plug`] Added `PowEmailConfirmation.Plug.sign_confirmation_token/2` to sign the `email_confirmation_token` to prevent timing attacks -* [`PowEmailConfirmation.Plug`] Added `PowEmailConfirmation.Plug.load_user_by_token/2` to verify the signed `email_confirmation_token` to prevent timing attacks -* [`PowEmailConfirmation.Plug`] Added `PowEmailConfirmation.Plug.confirm_email/2` with map as second argument -* [`PowInvitation.Plug`] Added `PowInvitation.Plug.sign_invitation_token/2` to sign the `invitation_token` -* [`PowInvitation.Plug`] Added `PowInvitation.Plug.load_invited_user_by_token/2` to verify the signed `invitation_token` to prevent timing attacks -* [`PowResetPassword.Plug`] Changed `PowResetPassword.Plug.create_reset_token/2` to sign the `:token` -* [`PowResetPassword.Plug`] Added `PowResetPassword.Plug.load_user_by_token/2` to verify the signed token to prevent timing attacks -* [`PowResetPassword.Plug`] Changed `PowResetPassword.Plug.update_user_password/2` so it decodes the signed token -* [`PowPersistentSession.Plug.Cookie`] Now uses signed tokens to prevent timing attacks -* [`Pow.Plug.Session`] Now uses signed session ID's to prevent timing attacks -* [`Pow.Plug`] Added `Pow.Plug.sign_token/4` to sign tokens -* [`Pow.Plug`] Added `Pow.Plug.verify_token/4` to decode and verify signed tokens -* [`Pow.Plug.MessageVerifier`] Added `Pow.Plug.MessageVerifier` module to sign and verify messages -* [`PowEmailConfirmation.Ecto.Context`] Added `PowEmailConfirmation.Ecto.Context.confirm_email/3` -* [`PowEmailConfirmation.Ecto.Schema`] Added `confirm_email_changeset/2` and `pow_confirm_email_changeset/2` to the macro -* [`PowEmailConfirmation.Ecto.Schema`] Added `PowEmailConfirmation.Ecto.Schema.confirm_email_changeset/2` -* [`PowInvitation.Ecto.Schema`] Added `accept_invitation_changeset/2` and `pow_accept_invitation_changeset/2` to the macro -* [`PowResetPassword.Ecto.Schema`] Added `reset_password_changeset/2` and `pow_reset_password_changeset/2` to the macro - -### Deprecations - -* [`PowEmailConfirmation.Plug`] `PowEmailConfirmation.Plug.confirm_email/2` with token param as second argument has been deprecated in favor of `PowEmailConfirmation.Plug.load_user_by_token/2`, and `PowEmailConfirmation.Plug.confirm_email/2` with map as second argument -* [`PowInvitation.Plug`] `PowInvitation.Plug.invited_user_from_token/2` has been deprecated in favor of `PowInvitation.Plug.load_invited_user_by_token/2` -* [`PowInvitation.Plug`] `PowInvitation.Plug.assign_invited_user/2` has been deprecated -* [`PowResetPassword.Plug`] `PowResetPassword.Plug.user_from_token/2` has been deprecated in favor of `PowResetPassword.Plug.load_user_by_token/2` -* [`PowResetPassword.Plug`] `PowResetPassword.Plug.assign_reset_password_user/2` has been deprecated -* [`PowEmailConfirmation.Ecto.Context`] `PowEmailConfirmation.Ecto.Context.confirm_email/2` deprecated in favor of `PowEmailConfirmation.Ecto.Context.confirm_email/3` -* [`PowEmailConfirmation.Ecto.Schema`] `PowEmailConfirmation.Ecto.Schema.confirm_email_changeset/1` deprecated in favor of `PowEmailConfirmation.Ecto.Schema.confirm_email_changeset/2` - -### Documentation - -* Updated the [API guide](guides/api.md) with signed tokens - -## v1.0.18 (2020-02-14) - -### Bug fixes - -* [`Pow.Phoenix.Routes`] Fixed bug where callback route methods is not using the overridden method -* [`PowPersistentSession.Plug.Cookie`] `PowPersistentSession.Plug.Cookie.delete/2` now correctly pulls token during `:before_send` callback -* [`Pow.Plug.Session`] `Pow.Plug.Session.delete/2` now correctly pulls session id during `:before_send` callback so `PowEmailConfirmation` will remove set session - -## v1.0.17 (2020-02-04) - -### Enhancements - -* [`Pow.Ecto.Context`] Calls to `Pow.Ecto.Context.get_by/2` replaced with `Pow.Operations.get_by/2` so custom users context module can be used. The following methods has been updated: - * `Pow.Ecto.Context.authenticate/2` - * `PowEmailConfirmation.Ecto.Context.get_by_confirmation_token/2` - * `PowInvitation.Ecto.Context.get_by_invitation_token/2` - * `PowResetPassword.Ecto.Context.get_by_email/2` -* [`Pow.Ecto.Schema.Changeset`] `Pow.Ecto.Schema.Changeset.confirm_password_changeset/3` now adds the default `Ecto.Changeset.validate_confirmation/3` error instead of the previous `not same as password` error -* [`Pow.Ecto.Schema.Changeset`] `Pow.Ecto.Schema.Changeset.confirm_password_changeset/3` now uses the `Ecto.Changeset.validate_confirmation/3` for validation and expects `:password_confirmation` instead of `:confirm_password` in params -* [`Pow.Ecto.Schema.Changeset`] `Pow.Ecto.Schema.Changeset.new_password_changeset/3` now only requires the `:password_hash` if there have been no previous errors set in the changeset -* [`Pow.Ecto.Schema`] No longer adds `:confirm_password` virtual field -* [`Pow.Ecto.Schema`] Now has an `@after_compile` callback that ensures all required fields has been defined -* [`PowInvitation.Phoenix.InvitationView`] Now renders `:password_confirmation` field instead of `:confirm_password` -* [`PowResetPassword.Phoenix.ResetPasswordView`] Now renders `:password_confirmation` field instead of `:confirm_password` -* [`Pow.Phoenix.RegistrationView`] Now renders `:password_confirmation` field instead of `:confirm_password` -* [`PowEmailConfirmation.Ecto.Schema`] No longer validates if `:email` has been taken before setting `:unconfirmed_email` -* [`PowEmailConfirmation.Phoenix.ControllerCallbacks`] Now prevents user enumeration for `PowInvitation.Phoenix.InvitationController.create/2` -* [`PowPersistentSession.Plug.Cookie`] Changed default cookie name to `persistent_session` -* [`PowPersistentSession.Plug.Cookie`] Removed renewal of cookie as the token will always expire -* [`PowPersistentSession.Plug.Cookie`] No longer expires invalid cookies -* [`Pow.Operations`] Added `Pow.Operations.fetch_primary_key_values/2` -* [`PowPersistentSession.Plug.Base`] Now registers `:before_send` callbacks -* [`PowPersistentSession.Plug.Cookie`] Now updates cookie and backend store in `:before_send` callback -* [`Pow.Plug.Base`] Now registers `:before_send` callbacks -* [`Pow.Plug.Session`] Now updates plug session and backend store in `:before_send` callback -* [`Pow.Plug`] Added `Pow.Plug.create/3` -* [`Pow.Plug`] Added `Pow.Plug.delete/2` - -### Removed - -* [`PowResetPassword.Phoenix.ResetPasswordController`] Will no longer prevent information leak by checking if `PowEmailConfirmation` or registration routes are enabled; instead it'll by default prevent user enumeration, but can be disabled if `pow_prevent_user_enumeration: false` is set in `conn.private` - -### Bug fixes - -* [`PowPersistentSession.Plug.Base`] With custom `:persistent_session_store` now falls back to `:cache_store_backend` configuration option -* [`PowResetPassword.Plug`] With custom `:reset_password_token_store` now falls back to `:cache_store_backend` configuration option -* [`Pow.Plug.Base`] With custom `:credentials_cache_store` now falls back to `:cache_store_backend` configuration option - -### Deprecations - -* [`Pow.Ecto.Changeset`] `Pow.Ecto.Schema.Changeset.confirm_password_changeset/3` has deprecated use of `:confirm_password` in params in favor of `:password_confirmation` -* [`Pow.Plug.Session`] `:session_store` option has been renamed to `:credentials_cache_store` -* [`Pow.Plug`] `Pow.Plug.clear_authenticated_user/1` deprecated in favor of `Pow.Plug.delete/1` - -## v1.0.16 (2020-01-07) - -**Note:** This release contains an important security fix. - -### Enhancements - -* [`PowPersistentSession.Plug.Cookie`] Now supports `:persistent_session_cookie_opts` to customize any options that will be passed on to `Plug.Conn.put_resp_cookie/4` -* [`PowResetPassword.Phoenix.ResetPasswordController`] Now uses `PowResetPassword.Phoenix.Messages.maybe_email_has_been_sent/1` with a generic response that tells the user the email has been sent only if an account was found -* [`PowResetPassword.Phoenix.ResetPasswordController`] When a user doesn't exist will now return success message if `PowEmailConfirmation` extension is enabled -* [`PowResetPassword.Phoenix.Messages`] Added `PowResetPassword.Phoenix.Messages.maybe_email_has_been_sent/1` and let `PowResetPassword.Phoenix.Messages.email_has_been_sent/1` fall back to it -* [`PowEmailConfirmation.Phoenix.ControllerCallbacks`] When a user tries to sign up and the email has already been taken the default e-mail confirmation required message will be shown -* [`Pow.Plug.Session`] Now renews the Plug session each time the Pow session is created or rolled - -### Bug fixes - -* [`Pow.Ecto.Schema.Changeset`] Fixed bug where `Pow.Ecto.Schema.Changeset.user_id_field_changeset/3` update with `nil` value caused an exception to be raised -* [`PowPersistentSession.Plug.Cookie`] Now expires the cookie 10 seconds after the last request when authenticating to prevent multiple simultaneous requests deletes the cookie immediately - -### Documentation - -* Added mailer rate limitation section to [production checklist guide](guides/production_checklist.md) -* [`Pow.Plug.Session`] Added section on session expiration to the docs -* Updated instructions in [umbrella project guide](guides/umbrella_project.md) to Elixir 1.9 -* [`Pow.Store.Backend.Base`] Updated usage example with Cachex -* Added [security practices page](guides/security_practices.md) - -## v1.0.15 (2019-11-20) - -### Enhancements - -* [`Pow.Extension.Base`] Extensions are now expected to have a base module with compile-time information whether certain modules are available to prevent unnecessary `Code.ensure_compiled?/1` calls: - * Added `Pow.Extension.Base` module - * Added `PowEmailConfirmation` module - * Added `PowInvitation` module - * Added `PowPersistentSession` module - * Added `PowResetPassword` module -* [`PowPersistentSession.Plug.Cookie`] Added support for custom metadata: - * `PowPersistentSession.Plug.Cookie.create/3` now stores a metadata keyword list that can be populated - * `PowPersistentSession.Plug.Cookie.create/3` will now, instead of adding `:session_fingerprint` to the metadata, populate the `:session_metadata` keyword list with `:fingerprint` - * `PowPersistentSession.Plug.Cookie.authenticate/2` will now populate session metadata with what exists in `:session_metadata` key for the persistent session metadata - * `PowPersistentSession.Plug.Cookie.create/3` now ensures to delete the previous persistent session first, if one is found in cookies -* [`Pow.Extension.Config`] Added `Pow.Extension.Config.extension_modules/2` - -### Bug fixes - -* [`Router.Phoenix.Router`] Fixed bug where resource routes were not filtered correctly according to the path bindings - -### Deprecations - -* [`Pow.Extension.Config`] Deprecated `Pow.Extension.Config.discover_modules/2` - -## v1.0.14 (2019-10-29) +## v1.1.0 (TBA) ### Changes -* Changed minmum password length to 8 (OWASP/NIST recommendations) -* `Pow.Phoenix.Router` now only filters routes that has equal number of bindings -* `Pow.Phoenix.Routes.user_not_authenticated_path/1` now only puts the `:request_path` param if the request is using "GET" method -* The stores has been refactored so the command conforms with ETS store. This means that put commands now accept `{key, value}` record element(s), and keys may be list for easier lookup. - * `Pow.Store.Backend.Base` behaviour now requires to; - * Accept `Pow.Store.Backend.Base.record/0` values for `put/2` - * Accept `Pow.Store.Backend.Base.key/0` for `delete/2` and `get/2` - * Implement `all/2` - * Remove `keys/1` - * Remove `put/3` - * `Pow.Store.Backend.EtsCache` now uses `:ordered_set` instead of `:set` for efficiency - * `Pow.Store.Backend.MnesiaCache` now uses `:ordered_set` instead of `:set` for efficiency - * `Pow.Store.Backend.MnesiaCache` will delete all binary key records when initialized - * `Pow.Store.Base` behaviour now requires to; - * Accept erlang term value for keys in all methods - * Implement `put/3` instead of `put/4` - * Implement `delete/2` instead of `put/3` - * Implement `get/2` instead of `put/3` - * Remove `keys/2` - * `Pow.Store.Base.all/3` added - * `Pow.Store.Base.put/3` added - * `Pow.Store.Base` will use binary key rather than key list if `all/2` doesn't exist in the backend cache - * Added `Pow.Store.CredentialsCache.users/2` - * Added `Pow.Store.CredentialsCache.sessions/2` - * `Pow.Store.CredentialsCache` now adds a session key rather than appending to a list for the user key to prevent race condition -* `Pow.Plug.Session.create/3` now stores a keyword list with metadata for the session rather than just a timestamp -* `Pow.Plug.Session.fetch/2` and `Pow.Plug.Session.create/3` now assigns `:pow_session_metadata` in `conn.private` with the session metadata -* `Pow.Plug.Session.create/3` will use the metadata found in `conn.private[:pow_session_metadata]` if it exists and otherwise add a randomly unique id for `:fingerprint` -* `PowPersistentSession.Plug.Cookie.create/3` will use the value of `conn.private[:pow_session_metadata][:fingerprint]` if it exists as `:session_fingerprint` in the persistent session metadata -* `PowPersistentSession.Plug.Cookie.authenticate/2` will assign `:fingerprint` to `conn.private[:pow_session_metadata]` if it exists in the persistent session metadata -* `Pow.Store.CredentialsCache.put/3` will invalidate any other sessions with the same `:fingerprint` if any is set in session metadata -* `PowResetPassword.Phoenix.ResetPasswordController.create/2` when a user doesn't exist will now only return success message if the registration routes has been disabled, otherwise the form with an error message will be returned -* Added `PowResetPassword.Phoenix.Messages.user_not_found/1` - -### Bug fixes - -* Fixed bug where `Pow.Store.CredentialsCache` wasn't used due to how `Pow.Store.Base` macro worked -* Fixed bug where `PowEmailConfirmation.Phoenix.ControllerCallbacks` couldn't deliver email +- Requires Elixir 1.7 or higher +- Requires Ecto 3.0 or higher +- Requires Phoenix 1.4.7 or higher ### Deprecations -* Deprecated `Pow.Store.Backend.EtsCache.keys/1` -* Deprecated `Pow.Store.Backend.EtsCache.put/3` -* Deprecated `Pow.Store.Backend.MnesiaCache.keys/1` -* Deprecated `Pow.Store.Backend.MnesiaCache.put/3` -* Deprecated `Pow.Store.Base.keys/2` -* Deprecated `Pow.Store.Base.put/4` -* Deprecated `Pow.Store.CredentialsCache.user_session_keys/3` -* Deprecated `Pow.Store.CredentialsCache.sessions/3` - -## v1.0.13 (2019-08-25) - -* Updated `PowEmailConfirmation.Ecto.Schema.changeset/3` so; - * when `:email` is identical to `:unconfirmed_email` it won't generate new `:email_confirmation_token` - * when `:email` is identical to the persisted `:email` value both `:email_confirmation_token` and `:unconfirmed_email` will be set to `nil` - * when there is no `:email` value in the params nothing happens -* Updated `PowEmailConfirmation.Ecto.Schema.confirm_email_changeset/1` so now `:email_confirmation_token` is set to `nil` -* Updated `Pow.Ecto.Schema.Changeset.user_id_field_changeset/3` so the e-mail validator now accepts unicode e-mails -* Added `PowEmailConfirmation.Ecto.Context.current_email_unconfirmed?/2` and `PowEmailConfirmation.Plug.pending_email_change?/1` -* Added `:email_validator` configuration option to `Pow.Ecto.Schema.Changeset` -* Added `Pow.Ecto.Schema.Changeset.validate_email/1` -* Fixed bug in `PowEmailConfirmation.Phoenix.ControllerCallbacks.send_confirmation_email/2` where the confirmation e-mail wasn't send to the updated e-mail address - -## v1.0.12 (2019-08-16) - -* Added API integration guide -* Added `:reset_password_token_store` configuration setting -* To prevent timing attacks, `Pow.Ecto.Context.authenticate/2` now verifies password on a blank user struct when no user can be found for the provided user id, but will always return nil. The blank user struct has a nil `:password_hash` value. The struct will be passed along with a blank password to the `verify_password/2` method in the user schema module. -* To prevent timing attacks, when `Pow.Ecto.Schema.Changeset.verify_password/3` receives a struct with a nil `:password_hash` value, it'll hash a blank password, but always return false. -* To prevent timing attacks, the UUID is always generated in `PowResetPassword.Plug.create_reset_token/2` whether the user exists or not. -* `PowPersistentSession.Plug.Base` now accepts `:persistent_session_ttl` which will pass the TTL to the cache backend and used for the max age of the sesion cookie in `PowPersistentSession.Plug.Cookie` -* Deprecated `:persistent_session_cookie_max_age` configuration setting -* `Pow.Store.Backend.MnesiaCache` can now auto join clusters -* `Pow.Store.Backend.MnesiaCache.Unsplit` module added for self-healing after network split -* Removed `:nodes` config option for `Pow.Store.Backend.MnesiaCache` - -## v1.0.11 (2019-06-13) - -* Fixed bug in router filters with Phoenix 1.4.7 - -## v1.0.10 (2019-06-09) - -* Prevent browser cache of `Pow.Phoenix.SessionController.new/2`, `Pow.Phoenix.RegistrationController.new/2` and `PowInvitation.Phoenix.InvitationController.edit/2` by setting "Cache-Control" header unless it already has been customized -* All links in docs generated with `mix docs` and on [hexdocs.pm](http://hexdocs.pm/pow/) now works -* Generated docs now uses lower case file name except for `README`, `CONTRIBUTING` and `CHANGELOG` -* Removed duplicate call for `Pow.Plug.Session.delete/2` in `Pow.Plug.Sesssion.create/3` - -## v1.0.9 (2019-06-04) - -### Changes - -* `Pow.Phoenix.Router` will now only add specific routes if there is no matching route already defined -* Added `Pow.Plug.get_plug/1` and instead of `:mod`, `:plug` is used in config -* `Pow.Ecto.Context.authenticate/2` now returns nil if user id or password is nil - -### Bug fixes - -* Fixed bug with exception raised in `Pow.Ecto.Schema.normalize_user_id_field_value/1` when calling `Pow.Ecto.Context.get_by/2` with a non binary user id -* Fixed bug with exception raised in `Pow.Ecto.Schema.normalize_user_id_field_value/1` when calling `Pow.Ecto.Context.authenticate/2` with a non binary user id - -### Deprecations - -* Deprecated `Pow.Plug.get_mod/1` -* Removed call to `Pow.Ecto.Context.repo/1` - -## v1.0.8 (2019-05-24) - -### Changes - -* Added support for layout in mails with `Pow.Phoenix.Mailer.Mail` by setting `conn.private[:pow_mailer_layout]` same way as the Phoenix layout with `conn.private[:phoenix_layout]` -* Added `:prefix` repo opts support to use in multitenant apps -* Removed `@changeset.data.__struct__.pow_user_id_field()` in template in favor of using `Pow.Ecto.Schema.user_id_field/1` - -### Bug fixes - -* Fixed bug in `Pow.Ecto.Schema.Changeset.current_password_changeset/3` where an exception would be thrown if the virtual `:current_password` field of the user struct was set and either the `:current_password` change was blank or identical - -### Deprecations - -* Deprecated `Mix.Pow.Ecto.Migration.create_migration_files/3` and moved it to `Mix.Pow.Ecto.Migration.create_migration_file/3` -* Deprecated `Pow.Ecto.Context.repo/1` and moved it to `Pow.Config.repo!/1` -* Deprecated `Pow.Ecto.Context.user_schema_mod/1` and moved it to `Pow.Config.user!/1` - -## v1.0.7 (2019-05-01) - -* Fixed bug with Phoenix 1.4.4 scoped routes - -## v1.0.6 (2019-04-19) - -* Fixed bug where custom layout setting raised exception in `Pow.Phoenix.ViewHelpers.layout/1` -* Prevent users from changing their email to one already taken when the PowEmailConfirmation extension has been enabled - -## v1.0.5 (2019-04-09) - -* Added `extension_messages/1` to extension controllers and callbacks -* Improved feedback for when no templates are generated for an extension with `mix pow.extension.phoenix.gen.templates` and `mix pow.extension.phoenix.mailer.gen.templates` tasks -* Error flash is no longer overridden in `Pow.Phoenix.PlugErrorHandler` if the error message is nil -* Fixed bug in the migration generator where `references/2` wasn't called with options -* Support any `:plug` version below `2.0.0` -* Deprecated `Pow.Extension.Ecto.Context.Base` - -## v1.0.4 (2019-03-13) - -* Added `PowInvitation` to the `mix pow.extension.phoenix.gen.templates` and `mix pow.extension.phoenix.mailer.gen.templates` tasks -* Fixed issue in umbrella projects where extensions wasn't found in environment configuration -* Fixed so `:namespace` environment config can be used as web app module name -* Shell instructions will only be printed if the configuration is missing -* Now requires that `:ecto` or `:phoenix` are included in the dependency list for the app to run respective mix tasks -* Deprecated `Mix.Pow.context_app/0` -* Deprecated `Mix.Pow.ensure_dep!/3` -* Deprecated `Mix.Pow.context_base/1` - -## v1.0.3 (2019-03-09) - -### Changes - -* Added `PowInvitation` extension -* Added support in `Pow.Ecto.Schema` for Ecto associations fields -* Added support for adding custom methods with `Pow.Extension.Ecto.Schema` through `__using__/1` macro in extension ecto schema module -* Help information raised with invalid schema arguments for `pow.install`, `pow.ecto.install`, `pow.ecto.gen.migration`, and `pow.ecto.gen.schema` mix tasks -* `PowEmailConfirmation` now redirects unconfirmed users to `after_registration_path/1` or `after_sign_in_path/1` rather than `pow_session_path(conn, :new)` - -### Bug fixes - -* Correct shell instructions for `mix pow.install` task with custom schema -* Fixed bug in `Pow.Extension.Phoenix.Router.Base` and `Pow.Extension.Phoenix.Messages` where the full extension name wasn't used to namespace methods - -### Deprecations - -* Deprecated `Pow.Extension.Config.underscore_extension/1` -* Deprecated `PowResetPassword.Ecto.Context.password_changeset/2` -* Deprecated `Pow.Ecto.Schema.filter_new_fields/2` -* Deprecated `:messages_backend_fallback` setting for extension controllers -* Removed deprecated macro `router_helpers/1` in `Pow.Phoenix.Controller` - -## v1.0.2 (2019-02-28) - -* Added flash error message about e-mail confirmation for when user changes e-mail with PowEmailConfirmation enabled -* Added `new_password_changeset/3` and `confirm_password_changeset/3` to `Pow.Ecto.Schema.Changeset` -* Redis cache store backend guide -* Correct shell instructions for `mix pow.phoenix.gen.templates` task -* Only load environment config in `Pow.Config.get/3` when no key is set in the provided config -* Fixed issue in `Pow.Store.Backend.MnesiaCache.keys/1` and `Pow.Store.Backend.EtsCache.keys/1` so they now return keys without namespace -* `Pow.Store.Backend.MnesiaCache.put/3` now raises an error if TTL is not provided - -### Breaking changes - -* `PowResetPassword.Plug.reset_password_token/1` has been removed - -## v1.0.1 (2019-01-27) - -* `pow.extension.ecto.gen.migrations` mix task will output warning when a migration file won't be generated for any particular extension -* Leading and trailing whitespace is removed from the user id field value (in addition to forced lower case) -* An exception is raised when `pow_routes/0` or `pow_extension_routes/0` are used inside router scopes with aliases -* Mail view templates assigns now has `[user: user, conn: conn]` along with the template specific assigns -* Mail view subject methods now gets the same assigns passed as mail view template assigns instead of only `[conn: conn]` -* Added `pow_registration_routes/0`, `pow_session_routes/0` and `pow_scope/1` macros to the router module -* Added guide on how to disable registration - -## v1.0.0 (2018-11-18) - -* Phoenix 1.4 support -* Ecto 3.0 support +- Removed deprecated method `PowResetPassword.Ecto.Context.password_changeset/2` +- Removed deprecated method `Pow.Extension.Config.underscore_extension/1` +- Removed deprecated method `Mix.Pow.context_app/0` +- Removed deprecated method `Mix.Pow.ensure_dep!/3` +- Removed deprecated method `Mix.Pow.context_base/1` +- Removed deprecated method `Mix.Pow.Ecto.Migration.create_migration_files/3` +- Removed deprecated method `Pow.Ecto.Context.repo/1` +- Removed deprecated method `Pow.Ecto.Context.user_schema_mod/1` +- Removed deprecated method `Pow.Plug.get_mod/1` +- Removed deprecated method `Pow.Plug.clear_authenticated_user/1` +- Removed deprecated method `Pow.Store.Backend.EtsCache.put/3` +- Removed deprecated method `Pow.Store.Backend.EtsCache.keys/1` +- Removed deprecated method `Pow.Store.Backend.MnesiaCache.put/3` +- Removed deprecated method `Pow.Store.Backend.MnesiaCache.keys/1` +- Removed deprecated method `Pow.Store.Base.keys/2` +- Removed deprecated method `Pow.Store.Base.put/4` +- Removed deprecated method `Pow.Store.CredentialsCache.sessions/3` +- Removed deprecated method `Pow.Store.CredentialsCache.user_session_keys/3` +- Removed deprecated method `Pow.Extension.Config.discover_modules/2` +- Removed deprecated method `PowEmailConfirmation.Ecto.Context.confirm_email/2` +- Removed deprecated method `PowEmailConfirmation.Ecto.Schema.confirm_email_changeset/1` +- Removed deprecated method `PowEmailConfirmation.Plug.confirm_email/2` +- Removed deprecated method `PowInvitation.Plug.invited_user_from_token/2` +- Removed deprecated method `PowInvitation.Plug.assign_invited_user/2` +- Removed deprecated method `PowResetPassword.Plug.assign_reset_password_user/2` +- Removed deprecated method `PowResetPassword.Plug.user_from_token/2` +- Removed deprecated `:session_store` configuration option for `Pow.Plug.Base`, `:credentials_cache_store` is used instead +- Removed deprecated `:messages_backend_fallback` configuration option for `Pow.Extension.Phoenix.Controller.Base` +- Removed deprecated `:persistent_session_cookie_max_age` configuration option for `PowPersistentSession.Plug.Cookie` +- Removed deprecated `:nodes` configuration option for `Pow.Store.Backend.MnesiaCache` +- Removed backwards compatibility in `Pow.Phoenix.Router` for routes generated with Phoenix `<= 1.4.6` +- Removed deprecated Bootstrap support in `Pow.Phoenix.HTML.FormTemplate` +- Removed deprecated module `Pow.Extension.Ecto.Context.Base` +- `Pow.Plug.Base` no longer sets `:mod` in the `:pow_config` private plug key +- `Pow.Plug.Session` no longer has backwards compatibility with `<= 1.0.13` session values +- `Pow.Store.Base` no longer has backwards compability with binary key cache backends +- `PowPersistentSession.Plug.Cookie` no longer has backwards compatibility with `<= 1.0.14` session values or `:session_fingerprint` metadata +- `Pow.Store.Base` macro no longer adds or supports overriding the following methods: + - `put/4` + - `delete/3` + - `get/3` +- `Pow.Store.Backend.MnesiaCache` no longer removes old deprecated records +- `Pow.Store.CredentialsCache` no longer handles deletion of deprecated records +- `Pow.Ecto.Schema.Changeset.confirm_password_changeset/3` no longer handles `:confirm_password` param +- `Pow.Extension.Base` no longer handles dynamic lookup, a base module is now required for all extensions +- `PowEmailConfirmation.Plug.confirm_email/2` no longer accepts binary (token) as second argument diff --git a/lib/extensions/email_confirmation/ecto/context.ex b/lib/extensions/email_confirmation/ecto/context.ex index 34b57026..aa3b0652 100644 --- a/lib/extensions/email_confirmation/ecto/context.ex +++ b/lib/extensions/email_confirmation/ecto/context.ex @@ -39,9 +39,4 @@ defmodule PowEmailConfirmation.Ecto.Context do |> user_mod.confirm_email_changeset(params) |> Context.do_update(config) end - - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use confirm_email/3 instead" - def confirm_email(user, config), do: confirm_email(user, %{}, config) end diff --git a/lib/extensions/email_confirmation/ecto/schema.ex b/lib/extensions/email_confirmation/ecto/schema.ex index 83a7455c..c9e1b4b7 100644 --- a/lib/extensions/email_confirmation/ecto/schema.ex +++ b/lib/extensions/email_confirmation/ecto/schema.ex @@ -172,9 +172,4 @@ defmodule PowEmailConfirmation.Ecto.Schema do |> Changeset.change(changes) |> Changeset.unique_constraint(:email) end - - # TODO: Remove by 1.1.0 - @deprecated "Use `confirm_email_changeset/2` instead" - @doc false - def confirm_email_changeset(changeset), do: confirm_email_changeset(changeset, %{}) end diff --git a/lib/extensions/email_confirmation/plug.ex b/lib/extensions/email_confirmation/plug.ex index 642367b5..24959f69 100644 --- a/lib/extensions/email_confirmation/plug.ex +++ b/lib/extensions/email_confirmation/plug.ex @@ -81,16 +81,6 @@ defmodule PowEmailConfirmation.Plug do {:ok, user} -> {:ok, user, maybe_renew_conn(conn, user, config)} end end - # TODO: Remove by 1.1.0 - def confirm_email(conn, token) when is_binary(token) do - IO.warn "#{unquote(__MODULE__)}.confirm_email/2 called with token is deprecated, use `load_user_by_token/2` and `confirm_email/2` with map as second argument instead" - - config = Plug.fetch_config(conn) - - token - |> Context.get_by_confirmation_token(config) - |> maybe_confirm_email(conn, config) - end defp confirm_email_user(conn) do conn.assigns[:confirm_email_user] @@ -110,15 +100,4 @@ defmodule PowEmailConfirmation.Plug do Keyword.equal?(clauses1, clauses2) end - - # TODO: Remove by 1.1.0 - defp maybe_confirm_email(nil, conn, _config), do: {:error, nil, conn} - defp maybe_confirm_email(user, conn, config) do - user - |> Context.confirm_email(%{}, config) - |> case do - {:error, changeset} -> {:error, changeset, conn} - {:ok, user} -> {:ok, user, maybe_renew_conn(conn, user, config)} - end - end end diff --git a/lib/extensions/invitation/plug.ex b/lib/extensions/invitation/plug.ex index 63ec0589..412e28c8 100644 --- a/lib/extensions/invitation/plug.ex +++ b/lib/extensions/invitation/plug.ex @@ -95,20 +95,4 @@ defmodule PowInvitation.Plug do _any -> {:error, conn} end end - - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use `load_invited_user_by_token/2` instead" - def invited_user_from_token(conn, token) do - config = Plug.fetch_config(conn) - - InvitationContext.get_by_invitation_token(token, config) - end - - # TODO: Remove by 1.1.0 - @doc false - @deprecated "No longer used" - def assign_invited_user(conn, user) do - Conn.assign(conn, :invited_user, user) - end end diff --git a/lib/extensions/persistent_session/plug/cookie.ex b/lib/extensions/persistent_session/plug/cookie.ex index 3bd9f94f..022f04f2 100644 --- a/lib/extensions/persistent_session/plug/cookie.ex +++ b/lib/extensions/persistent_session/plug/cookie.ex @@ -230,9 +230,6 @@ defmodule PowPersistentSession.Plug.Cookie do user -> {token, {user, metadata}} end end - # TODO: Remove by 1.1.0 - defp fetch_user({token, user_id}, config), - do: fetch_user({token, {user_id, []}}, config) defp filter_invalid!([id: _value] = clauses), do: clauses defp filter_invalid!(clauses), do: raise "Invalid get_by clauses stored: #{inspect clauses}" @@ -287,7 +284,7 @@ defmodule PowPersistentSession.Plug.Cookie do defp update_session_metadata(conn, metadata) do case Keyword.get(metadata, :session_metadata) do nil -> - fallback_session_fingerprint(conn, metadata) + conn session_metadata -> metadata = Map.get(conn.private, :pow_session_metadata, []) @@ -296,22 +293,6 @@ defmodule PowPersistentSession.Plug.Cookie do end end - # TODO: Remove by 1.1.0 - defp fallback_session_fingerprint(conn, metadata) do - case Keyword.get(metadata, :session_fingerprint) do - nil -> - conn - - fingerprint -> - metadata = - conn.private - |> Map.get(:pow_session_metadata, []) - |> Keyword.put(:fingerprint, fingerprint) - - Conn.put_private(conn, :pow_session_metadata, metadata) - end - end - defp gen_token(config) do uuid = UUID.generate() @@ -361,17 +342,8 @@ defmodule PowPersistentSession.Plug.Cookie do end defp max_age(config) do - # TODO: Remove by 1.1.0 - case Config.get(config, :persistent_session_cookie_max_age) do - nil -> - config - |> PowPersistentSession.Plug.Base.ttl() - |> Integer.floor_div(1000) - - max_age -> - IO.warn("use of `:persistent_session_cookie_max_age` config value in #{inspect unquote(__MODULE__)} is deprecated, please use `:persistent_session_ttl`") - - max_age - end + config + |> PowPersistentSession.Plug.Base.ttl() + |> Integer.floor_div(1000) end end diff --git a/lib/extensions/reset_password/ecto/context.ex b/lib/extensions/reset_password/ecto/context.ex index afe83310..562702a5 100644 --- a/lib/extensions/reset_password/ecto/context.ex +++ b/lib/extensions/reset_password/ecto/context.ex @@ -1,7 +1,6 @@ defmodule PowResetPassword.Ecto.Context do @moduledoc false alias Pow.{Config, Ecto.Context, Operations} - alias PowResetPassword.Ecto.Schema @spec get_by_email(binary(), Config.t()) :: Context.user() | nil def get_by_email(email, config), do: Operations.get_by([email: email], config) @@ -12,8 +11,4 @@ defmodule PowResetPassword.Ecto.Context do |> user_mod.reset_password_changeset(params) |> Context.do_update(config) end - - # TODO: Remove by 1.1.0 - @deprecated "Use `PowResetPassword.Ecto.Schema.reset_password_changeset/2` instead" - def password_changeset(user, params), do: Schema.reset_password_changeset(user, params) end diff --git a/lib/extensions/reset_password/plug.ex b/lib/extensions/reset_password/plug.ex index a04cd43b..f86c6e3c 100644 --- a/lib/extensions/reset_password/plug.ex +++ b/lib/extensions/reset_password/plug.ex @@ -24,13 +24,6 @@ defmodule PowResetPassword.Plug do |> struct() end - # TODO: Remove by 1.1.0 - @doc false - @deprecated "No longer used" - def assign_reset_password_user(conn, user) do - Conn.assign(conn, :reset_password_user, user) - end - @doc """ Finds a user for the provided params, creates a token, and stores the user for the token. @@ -104,23 +97,6 @@ defmodule PowResetPassword.Plug do end end - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use `load_user_by_token/2` instead" - def user_from_token(conn, token) do - {store, store_config} = - conn - |> Plug.fetch_config() - |> store() - - store_config - |> store.get(token) - |> case do - :not_found -> nil - user -> user - end - end - @doc """ Updates the password for the user fetched in the connection. diff --git a/lib/mix/pow.ex b/lib/mix/pow.ex index 034205a0..28af5d56 100644 --- a/lib/mix/pow.ex +++ b/lib/mix/pow.ex @@ -16,22 +16,6 @@ defmodule Mix.Pow do :ok end - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use `ensure_ecto!` or `ensure_phoenix!` instead" - @spec ensure_dep!(binary(), atom(), OptionParser.argv()) :: :ok | no_return - def ensure_dep!(task, dep, _args) do - fetch_deps() - |> top_level_dep_in_deps?(dep) - |> case do - true -> - :ok - - false -> - Mix.raise("mix #{task} can only be run inside an application directory that has #{inspect dep} as dependency") - end - end - @doc """ Raises an exception if application doesn't have Ecto as dependency. """ @@ -53,15 +37,7 @@ defmodule Mix.Pow do end) end - # TODO: Remove by 1.1.0 and only support Elixir 1.7 - defp fetch_deps do - System.version() - |> Version.match?("~> 1.6.0") - |> case do - true -> apply(Dep, :loaded, [[]]) - false -> apply(Dep, :load_on_environment, [[]]) - end - end + defp fetch_deps, do: Dep.load_on_environment([]) @doc """ Raises an exception if application doesn't have Phoenix as dependency. @@ -147,36 +123,12 @@ defmodule Mix.Pow do """) end - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Please use `Pow.Phoenix.parse_structure/1` instead" - @spec context_app :: atom() | no_return - def context_app do - this_app = otp_app() - - this_app - |> Application.get_env(:generators, []) - |> Keyword.get(:context_app) - |> case do - nil -> this_app - false -> Mix.raise("No context_app configured for current application") - {app, _path} -> app - app -> app - end - end - @doc false @spec otp_app :: atom() | no_return def otp_app do Keyword.fetch!(Mix.Project.config(), :app) end - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use `app_base/1` instead" - @spec context_base(atom()) :: atom() - def context_base(app), do: app_base(app) - @doc """ Fetches the context base module for the app. """ diff --git a/lib/mix/pow/ecto/migration.ex b/lib/mix/pow/ecto/migration.ex index 94d00d39..0e0b1269 100644 --- a/lib/mix/pow/ecto/migration.ex +++ b/lib/mix/pow/ecto/migration.ex @@ -2,12 +2,7 @@ defmodule Mix.Pow.Ecto.Migration do @moduledoc """ Utilities module for ecto migrations in mix tasks. """ - alias Mix.Generator - - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use `create_migration_file/3`" - defdelegate create_migration_files(repo, name, content), to: __MODULE__, as: :create_migration_file + alias Mix.{EctoSQL, Generator} @doc """ Creates a migration file for a repo. @@ -17,7 +12,7 @@ defmodule Mix.Pow.Ecto.Migration do base_name = "#{Macro.underscore(name)}.exs" path = repo - |> source_repo_priv() + |> EctoSQL.source_repo_priv() |> Path.join("migrations") |> maybe_create_directory() timestamp = timestamp(path) @@ -68,14 +63,4 @@ defmodule Mix.Pow.Ecto.Migration do defp pad(i) when i < 10, do: <> defp pad(i), do: to_string(i) - - # TODO: Remove by 1.1.0 and only use Ecto 3.0 - defp source_repo_priv(repo) do - mod = - if Pow.dependency_vsn_match?(:ecto, "< 3.0.0"), - do: Mix.Ecto, - else: Mix.EctoSQL - - mod.source_repo_priv(repo) - end end diff --git a/lib/pow.ex b/lib/pow.ex index b8e6462c..614122b4 100644 --- a/lib/pow.ex +++ b/lib/pow.ex @@ -1,19 +1,3 @@ defmodule Pow do @moduledoc false - - @doc """ - Checks for version requirement in dependencies. - """ - @spec dependency_vsn_match?(atom(), binary()) :: boolean() - def dependency_vsn_match?(dep, req) do - case :application.get_key(dep, :vsn) do - {:ok, actual} -> - actual - |> List.to_string() - |> Version.match?(req) - - _any -> - false - end - end end diff --git a/lib/pow/ecto/context.ex b/lib/pow/ecto/context.ex index e2de97a7..d7d34895 100644 --- a/lib/pow/ecto/context.ex +++ b/lib/pow/ecto/context.ex @@ -236,17 +236,9 @@ defmodule Pow.Ecto.Context do end end - # TODO: Remove by 1.1.0 - @deprecated "Use `Pow.Config.repo!/1` instead" - defdelegate repo(config), to: Config, as: :repo! - defp repo_opts(config, opts) do config |> Config.get(:repo_opts, []) |> Keyword.take(opts) end - - # TODO: Remove by 1.1.0 - @deprecated "Use `Pow.Config.user!/1` instead" - defdelegate user_schema_mod(config), to: Config, as: :user! end diff --git a/lib/pow/ecto/schema.ex b/lib/pow/ecto/schema.ex index 0edc51d7..3d170bfd 100644 --- a/lib/pow/ecto/schema.ex +++ b/lib/pow/ecto/schema.ex @@ -278,10 +278,6 @@ defmodule Pow.Ecto.Schema do Enum.filter(fields, ¬ Enum.member?(existing_fields, {elem(&1, 0), elem(&1, 1)})) end - # TODO: Remove by 1.1.0 - @deprecated "No longer public method" - def filter_new_fields(fields, existing_fields), do: __filter_new_fields__(fields, existing_fields) - @doc false defmacro __register_fields__ do quote do diff --git a/lib/pow/ecto/schema/changeset.ex b/lib/pow/ecto/schema/changeset.ex index ba051cf0..f19d5f17 100644 --- a/lib/pow/ecto/schema/changeset.ex +++ b/lib/pow/ecto/schema/changeset.ex @@ -87,7 +87,6 @@ defmodule Pow.Ecto.Schema.Changeset do |> maybe_validate_password_hash() end - # TODO: Remove `confirm_password` support by 1.1.0 @doc """ Validates the confirm password field. @@ -96,39 +95,7 @@ defmodule Pow.Ecto.Schema.Changeset do `nil`. """ @spec confirm_password_changeset(Ecto.Schema.t() | Changeset.t(), map(), Config.t()) :: Changeset.t() - def confirm_password_changeset(user_or_changeset, %{confirm_password: password_confirmation} = params, _config) do - params = - params - |> Map.delete(:confirm_password) - |> Map.put(:password_confirmation, password_confirmation) - - do_confirm_password_changeset(user_or_changeset, params) - end - def confirm_password_changeset(user_or_changeset, %{"confirm_password" => password_confirmation} = params, _config) do - params = - params - |> Map.delete("confirm_password") - |> Map.put("password_confirmation", password_confirmation) - - convert_confirm_password_param(user_or_changeset, params) - end - def confirm_password_changeset(user_or_changeset, params, _config), - do: do_confirm_password_changeset(user_or_changeset, params) - - # TODO: Remove by 1.1.0 - defp convert_confirm_password_param(user_or_changeset, params) do - IO.warn("warning: passing `confirm_password` value to `#{inspect unquote(__MODULE__)}.confirm_password_changeset/3` has been deprecated, please use `password_confirmation` instead") - - changeset = do_confirm_password_changeset(user_or_changeset, params) - errors = Enum.map(changeset.errors, fn - {:password_confirmation, error} -> {:confirm_password, error} - error -> error - end) - - %{changeset | errors: errors} - end - - defp do_confirm_password_changeset(user_or_changeset, params) do + def confirm_password_changeset(user_or_changeset, params, _config) do changeset = Changeset.cast(user_or_changeset, params, [:password]) changeset diff --git a/lib/pow/extension/base.ex b/lib/pow/extension/base.ex index 91ce21d7..fe1021d8 100644 --- a/lib/pow/extension/base.ex +++ b/lib/pow/extension/base.ex @@ -2,9 +2,6 @@ defmodule Pow.Extension.Base do @moduledoc """ Used to set up extensions to enable parts of extension for auto-discovery. - This exists to prevent unnecessary `Code.ensure_compiled?/1` calls, and will - let the extension define what modules it has. - ## Usage defmodule MyCustomExtension do @@ -51,59 +48,22 @@ defmodule Pow.Extension.Base do @doc """ Checks whether an extension has a certain module. - - If a base extension module doesn't exist, or is configured improperly, - `Code.ensure_compiled?/1` will be used instead to see whether the module - exists for the extension. """ @spec has?(atom(), [any()]) :: boolean() - def has?(extension, module_list) do - try do - has_extension_module?(extension, module_list) - rescue - # TODO: Remove or refactor by 1.1.0 - _e in UndefinedFunctionError -> - IO.warn("no #{inspect extension} base module to check for #{inspect module_list} support found, please use #{inspect __MODULE__} to implement it") - - [extension] - |> Kernel.++(module_list) - |> Module.concat() - |> Code.ensure_compiled?() - end - end - - defp has_extension_module?(extension, ["Ecto", "Schema"]), do: extension.ecto_schema?() - defp has_extension_module?(extension, ["Phoenix", "ControllerCallbacks"]), do: extension.phoenix_controller_callbacks?() - defp has_extension_module?(extension, ["Phoenix", "Messages"]), do: extension.phoenix_messages?() - defp has_extension_module?(extension, ["Phoenix", "Router"]), do: extension.phoenix_router?() + def has?(extension, ["Ecto", "Schema"]), do: extension.ecto_schema?() + def has?(extension, ["Phoenix", "ControllerCallbacks"]), do: extension.phoenix_controller_callbacks?() + def has?(extension, ["Phoenix", "Messages"]), do: extension.phoenix_messages?() + def has?(extension, ["Phoenix", "Router"]), do: extension.phoenix_router?() @doc """ Checks whether an extension has a certain module that has a `__using__/1` macro. - - This calls `has?/2` first, If a base extension module doesn't exist, or is - configured improperly, `Kernel.macro_exported?/3` will be used instead to - check if the module has a `__using__/1` macro. """ @spec use?(atom(), [any()]) :: boolean() def use?(extension, module_list) do case has?(extension, module_list) do - true -> - try do - use_extension_module?(extension, module_list) - rescue - # TODO: Remove or refactor by 1.1.0 - _e in UndefinedFunctionError -> - IO.warn("#{inspect extension} has been configured improperly") - - [extension] - |> Kernel.++(module_list) - |> Module.concat() - |> Kernel.macro_exported?(:__using__, 1) - end - - false -> - false + true -> use_extension_module?(extension, module_list) + false -> false end end diff --git a/lib/pow/extension/config.ex b/lib/pow/extension/config.ex index 655c91d4..a8f63d26 100644 --- a/lib/pow/extension/config.ex +++ b/lib/pow/extension/config.ex @@ -25,28 +25,4 @@ defmodule Pow.Extension.Config do |> Enum.filter(&Base.has?(&1, module_list)) |> Enum.map(&Module.concat([&1] ++ module_list)) end - - # TODO: Remove by 1.1.0 - @deprecated "Use `extension_modules/2` instead" - def discover_modules(config, module_list) do - config - |> extensions() - |> extension_modules(module_list) - end - - # TODO: Remove by 1.1.0 - @doc """ - Returns a binary of the extension atom. - - This is usually used to create extension namespaces for methods to be used - in shared modules. - """ - @deprecated "Create the namespace directly in your module" - @spec underscore_extension(atom()) :: binary() - def underscore_extension(extension) do - extension - |> Module.split() - |> List.first() - |> Macro.underscore() - end end diff --git a/lib/pow/extension/ecto/context/base.ex b/lib/pow/extension/ecto/context/base.ex deleted file mode 100644 index 69dee98d..00000000 --- a/lib/pow/extension/ecto/context/base.ex +++ /dev/null @@ -1,15 +0,0 @@ -defmodule Pow.Extension.Ecto.Context.Base do - # TODO: Remove by 1.1.0 - @moduledoc false - alias Pow.Ecto.Context - - @doc false - defmacro __using__(_opts) do - quote do - IO.warn("use #{unquote(__MODULE__)} is deprecated, please use methods in #{Context} instead") - - defdelegate user_schema_mod(config), to: Context - defdelegate repo(config), to: Context - end - end -end diff --git a/lib/pow/extension/phoenix/controllers/controller/base.ex b/lib/pow/extension/phoenix/controllers/controller/base.ex index 3ad61f6c..621f2c52 100644 --- a/lib/pow/extension/phoenix/controllers/controller/base.ex +++ b/lib/pow/extension/phoenix/controllers/controller/base.ex @@ -10,21 +10,21 @@ defmodule Pow.Extension.Phoenix.Controller.Base do # ... end """ - alias Pow.{Config, Phoenix.Controller} + alias Pow.Phoenix.Controller @doc false defmacro __using__(config) do quote do use Controller, unquote(config) - unquote(__MODULE__).__define_helper_methods__(unquote(config)) + unquote(__MODULE__).__define_helper_methods__() end end @doc false - defmacro __define_helper_methods__(config) do + defmacro __define_helper_methods__() do quote do - @messages_fallback unquote(__MODULE__).__messages_fallback__(unquote(config), __MODULE__, __ENV__) + @messages_fallback unquote(__MODULE__).__messages_fallback__(__MODULE__) @doc false def extension_messages(conn), do: unquote(__MODULE__).__messages_module__(conn, @messages_fallback) @@ -51,17 +51,4 @@ defmodule Pow.Extension.Phoenix.Controller.Base do |> Enum.reverse() |> Module.concat() end - - # TODO: Remove config fallback by 1.1.0 - def __messages_fallback__(config, module, env) do - case Config.get(config, :messages_backend_fallback) do - nil -> - __messages_fallback__(module) - - module -> - IO.warn("Passing `:messages_backend_fallback` is deprecated", Macro.Env.stacktrace(env)) - - module - end - end end diff --git a/lib/pow/extension/phoenix/controllers/controller_callbacks/base.ex b/lib/pow/extension/phoenix/controllers/controller_callbacks/base.ex index 833f853e..92dd0806 100644 --- a/lib/pow/extension/phoenix/controllers/controller_callbacks/base.ex +++ b/lib/pow/extension/phoenix/controllers/controller_callbacks/base.ex @@ -19,14 +19,14 @@ defmodule Pow.Extension.Phoenix.ControllerCallbacks.Base do @callback before_respond(atom(), atom(), any(), Config.t()) :: any() @doc false - defmacro __using__(config) do + defmacro __using__(_config) do quote do @behaviour unquote(__MODULE__) require Base require Controller - Base.__define_helper_methods__(unquote(config)) + Base.__define_helper_methods__() Controller.__define_helper_methods__() @before_compile unquote(__MODULE__) diff --git a/lib/pow/phoenix/html/bootstrap.ex b/lib/pow/phoenix/html/bootstrap.ex deleted file mode 100644 index b9c1a123..00000000 --- a/lib/pow/phoenix/html/bootstrap.ex +++ /dev/null @@ -1,54 +0,0 @@ -defmodule Pow.Phoenix.HTML.Bootstrap do - # TODO: Remove module by 1.1.0 and only support Phoenix 1.4.0 - - @moduledoc """ - Module that helps build HTML for Phoenix with Bootstrap CSS. - """ - import Pow.Phoenix.HTML.FormTemplate, only: [inspect_key: 1] - - @form_template EEx.compile_string( - """ - <%%= form_for @changeset, @action, [as: :user], fn f -> %> - <%%= if @changeset.action do %> -
-

Oops, something went wrong! Please check the errors below.

-
- <%% end %> - <%= for {label, input, error} <- inputs, input do %> -
- <%= label %> - <%= input %> - <%= error %> -
- <% end %> -
- <%%= submit <%= inspect button_label %>, class: "btn btn-primary" %> -
- <%% end %> - """) - - @doc """ - Renders a form. - """ - @spec render_form(list(), binary()) :: Macro.t() - def render_form(inputs, button_label) do - inputs = for {type, key} <- inputs, do: input(type, key) - - unquote(@form_template) - end - - defp input(:text, key) do - {label(key), ~s(<%= text_input f, #{inspect_key(key)}, class: "form-control" %>), error(key)} - end - defp input(:password, key) do - {label(key), ~s(<%= password_input f, #{inspect_key(key)}, class: "form-control" %>), error(key)} - end - - defp label(key) do - ~s(<%= label f, #{inspect_key(key)}, class: "control-label" %>) - end - - defp error(key) do - ~s(<%= error_tag f, #{inspect_key(key)} %>) - end -end diff --git a/lib/pow/phoenix/html/form_template.ex b/lib/pow/phoenix/html/form_template.ex index 93d98f22..cef04c74 100644 --- a/lib/pow/phoenix/html/form_template.ex +++ b/lib/pow/phoenix/html/form_template.ex @@ -1,11 +1,7 @@ defmodule Pow.Phoenix.HTML.FormTemplate do @moduledoc """ Module that can build user form templates for Phoenix. - - For Phoenix 1.3, or bootstrap templates, `Pow.Phoenix.HTML.Bootstrap` can be - used. """ - alias Pow.Phoenix.HTML.Bootstrap @template EEx.compile_string( """ @@ -32,24 +28,12 @@ defmodule Pow.Phoenix.HTML.FormTemplate do ## Options * `:button_label` - the submit button label, defaults to "Submit". - * `:bootstrap` - to render form as bootstrap, defaults to false with - phoenix 1.4 and true with phoenix 1.3. """ @spec render(list(), Keyword.t()) :: Macro.t() def render(inputs, opts \\ []) do button_label = Keyword.get(opts, :button_label, "Submit") - case bootstrap?(opts) do - true -> Bootstrap.render_form(inputs, button_label) - _any -> render_form(inputs, button_label) - end - end - - # TODO: Remove bootstrap support by 1.1.0 and only support Phoenix 1.4.0 - defp bootstrap?(opts) do - bootstrap = Pow.dependency_vsn_match?(:phoenix, "~> 1.3.0") - - Keyword.get(opts, :bootstrap, bootstrap) + render_form(inputs, button_label) end defp render_form(inputs, button_label) do diff --git a/lib/pow/phoenix/router.ex b/lib/pow/phoenix/router.ex index af5adefe..cf4414dc 100644 --- a/lib/pow/phoenix/router.ex +++ b/lib/pow/phoenix/router.ex @@ -201,13 +201,6 @@ defmodule Pow.Phoenix.Router do |> case do %{plug_opts: _, helper: _} = route -> any_matching_routes?(phoenix_routes, route, [:plug_opts, :helper]) - - # TODO: Remove this match by 1.1.0, and up requirement for Phoenix to minimum 1.4.7 - %{opts: _, helper: _} = route -> - any_matching_routes?(phoenix_routes, route, [:opts, :helper]) - - _any -> - false end end diff --git a/lib/pow/plug.ex b/lib/pow/plug.ex index ffc432c3..3b802952 100644 --- a/lib/pow/plug.ex +++ b/lib/pow/plug.ex @@ -99,12 +99,6 @@ defmodule Pow.Plug do end end - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use `delete/1` instead" - @spec clear_authenticated_user(Conn.t()) :: {:ok, Conn.t()} - def clear_authenticated_user(conn), do: {:ok, delete(conn)} - @doc """ Creates a changeset from the current authenticated user. """ @@ -172,12 +166,6 @@ defmodule Pow.Plug do {:error, changeset, conn} end - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use `get_plug/1` instead" - @spec get_mod(Config.t()) :: atom() - def get_mod(config), do: get_plug(config) - @spec get_plug(Config.t()) :: atom() def get_plug(config) do config[:plug] || no_plug_error() diff --git a/lib/pow/plug/base.ex b/lib/pow/plug/base.ex index a42ab570..26550324 100644 --- a/lib/pow/plug/base.ex +++ b/lib/pow/plug/base.ex @@ -98,11 +98,7 @@ defmodule Pow.Plug.Base do Conn.put_private(conn, @before_send_private_key, callbacks) end - defp put_plug(config) do - config - |> Config.put(:plug, __MODULE__) - |> Config.put(:mod, __MODULE__) # TODO: Remove by 1.1.0, this is only for backwards compability - end + defp put_plug(config), do: Config.put(config, :plug, __MODULE__) @doc """ Calls `fetch/2` and assigns the current user to the conn. @@ -154,27 +150,12 @@ defmodule Pow.Plug.Base do @spec store(Config.t()) :: {module(), Config.t()} def store(config) do - config - |> Config.get(:credentials_cache_store) - |> Kernel.||(fallback_store(config)) - |> case do + case Config.get(config, :credentials_cache_store) do {store, store_config} -> {store, store_opts(config, store_config)} nil -> {CredentialsCache, store_opts(config)} end end - # TODO: Remove by 1.1.0 - defp fallback_store(config) do - case Config.get(config, :session_store) do - nil -> - nil - - value -> - IO.warn("use of `:session_store` config value is deprecated, use `:credentials_cache_store` instead") - value - end - end - defp store_opts(config, store_config \\ []) do Keyword.put_new(store_config, :backend, Config.get(config, :cache_store_backend, EtsCache)) end diff --git a/lib/pow/plug/session.ex b/lib/pow/plug/session.ex index 365d1f29..46ddc635 100644 --- a/lib/pow/plug/session.ex +++ b/lib/pow/plug/session.ex @@ -141,9 +141,9 @@ defmodule Pow.Plug.Session do defp fetch(conn, session_id, config) do {store, store_config} = store(config) - {session_id, store.get(store_config, session_id)} - |> convert_old_session_value() - |> handle_fetched_session_value(conn, config) + store_config + |> store.get(session_id) + |> renew_stale_session(session_id, conn, config) end @doc """ @@ -233,18 +233,10 @@ defmodule Pow.Plug.Session do end) end - # TODO: Remove by 1.1.0 - defp convert_old_session_value({session_id, {user, timestamp}}) when is_number(timestamp), do: {session_id, {user, inserted_at: timestamp}} - defp convert_old_session_value(any), do: any + defp renew_stale_session(:not_found, _session_id, conn, _config), do: {conn, nil} + defp renew_stale_session({user, metadata}, session_id, conn, config) when is_list(metadata) do + conn = Conn.put_private(conn, :pow_session_metadata, metadata) - defp handle_fetched_session_value({_session_id, :not_found}, conn, _config), do: {conn, nil} - defp handle_fetched_session_value({session_id, {user, metadata}}, conn, config) when is_list(metadata) do - conn - |> Conn.put_private(:pow_session_metadata, metadata) - |> renew_stale_session(session_id, user, metadata, config) - end - - defp renew_stale_session(conn, session_id, user, metadata, config) do metadata |> Keyword.get(:inserted_at) |> session_stale?(config) diff --git a/lib/pow/store/backend/ets_cache.ex b/lib/pow/store/backend/ets_cache.ex index 12f2ea42..315daec2 100644 --- a/lib/pow/store/backend/ets_cache.ex +++ b/lib/pow/store/backend/ets_cache.ex @@ -162,14 +162,4 @@ defmodule Pow.Store.Backend.EtsCache do Map.delete(invalidators, key) end - - # TODO: Remove by 1.1.0 - @deprecated "Use `put/2` instead" - @doc false - def put(config, key, value), do: put(config, {key, value}) - - # TODO: Remove by 1.1.0 - @deprecated "Use `all/2` instead" - @doc false - def keys(config), do: all(config, :_) end diff --git a/lib/pow/store/backend/mnesia_cache.ex b/lib/pow/store/backend/mnesia_cache.ex index 93533c03..5306572b 100644 --- a/lib/pow/store/backend/mnesia_cache.ex +++ b/lib/pow/store/backend/mnesia_cache.ex @@ -97,12 +97,6 @@ defmodule Pow.Store.Backend.MnesiaCache do @spec start_link(Config.t()) :: GenServer.on_start() def start_link(config) do - # TODO: Remove by 1.1.0 - case Config.get(config, :nodes) do - nil -> :ok - _nodes -> IO.warn("use of `:nodes` config value for #{inspect unquote(__MODULE__)} is no longer used") - end - GenServer.start_link(__MODULE__, config, name: __MODULE__) end @@ -425,14 +419,6 @@ defmodule Pow.Store.Backend.MnesiaCache do |> unwrap() |> append_invalidator(invalidators, ttl, config) - # TODO: Remove by 1.1.0 - {@mnesia_cache_tab, key, {_key, _value, _config, expire}}, invalidators when is_binary(key) and is_number(expire) -> - Logger.warn("[#{inspect __MODULE__}] Deleting old record #{inspect key}") - - :mnesia.delete({@mnesia_cache_tab, key}) - - invalidators - {@mnesia_cache_tab, key, _value}, invalidators -> Logger.warn("[#{inspect __MODULE__}] Found unexpected record #{inspect key}, please delete it") @@ -462,14 +448,4 @@ defmodule Pow.Store.Backend.MnesiaCache do @spec raise_ttl_error :: no_return defp raise_ttl_error, do: Config.raise_error("`:ttl` configuration option is required for #{inspect(__MODULE__)}") - - # TODO: Remove by 1.1.0 - @deprecated "Use `put/2` instead" - @doc false - def put(config, key, value), do: put(config, {key, value}) - - # TODO: Remove by 1.1.0 - @deprecated "Use `all/2` instead" - @doc false - def keys(config), do: all(config, :_) end diff --git a/lib/pow/store/base.ex b/lib/pow/store/base.ex index c9bf1749..89a9321c 100644 --- a/lib/pow/store/base.ex +++ b/lib/pow/store/base.ex @@ -16,7 +16,7 @@ defmodule Pow.Store.Base do end """ alias Pow.Config - alias Pow.Store.Backend.{EtsCache, MnesiaCache, Base} + alias Pow.Store.Backend.{EtsCache, Base} @type key :: Base.key() @type record :: Base.record() @@ -61,155 +61,33 @@ defmodule Pow.Store.Base do end defoverridable unquote(__MODULE__) - - # TODO: Remove by 1.1.0 - @doc false - def put(config, backend_config, key, value) do - config - |> merge_backend_config(backend_config) - |> put(key, value) - end - - defp merge_backend_config(config, backend_config) do - backend_config = Keyword.take(backend_config, [:ttl, :namespace]) - - Keyword.merge(config, backend_config) - end - - # TODO: Remove by 1.1.0 - @doc false - def delete(config, backend_config, key) do - config - |> merge_backend_config(backend_config) - |> delete(key) - end - - # TODO: Remove by 1.1.0 - @doc false - def get(config, backend_config, key) do - config - |> merge_backend_config(backend_config) - |> get(key) - end - - # TODO: Remove by 1.1.0 - defoverridable put: 4, delete: 3, get: 3 end end @spec put(Config.t(), Config.t(), record() | [record()]) :: :ok def put(config, backend_config, record_or_records) do - # TODO: Update by 1.1.0 - backwards_compatible_call(store(config), :put, [backend_config, record_or_records]) + store(config).put(backend_config, record_or_records) end @doc false @spec delete(Config.t(), Config.t(), key()) :: :ok def delete(config, backend_config, key) do - # TODO: Update by 1.1.0 - backwards_compatible_call(store(config), :delete, [backend_config, key]) + store(config).delete(backend_config, key) end @doc false @spec get(Config.t(), Config.t(), key()) :: any() | :not_found def get(config, backend_config, key) do - # TODO: Update by 1.1.0 - backwards_compatible_call(store(config), :get, [backend_config, key]) + store(config).get(backend_config, key) end @doc false @spec all(Config.t(), Config.t(), key_match()) :: [record()] def all(config, backend_config, key_match) do - # TODO: Update by 1.1.0 - backwards_compatible_call(store(config), :all, [backend_config, key_match]) + store(config).all(backend_config, key_match) end defp store(config) do Config.get(config, :backend, EtsCache) end - - # TODO: Remove by 1.1.0 - defp backwards_compatible_call(store, method, args) do - store - |> has_binary_keys?() - |> case do - false -> - apply(store, method, args) - - true -> - IO.warn("binary key for backend stores is depecated, update `#{store}` to accept erlang terms instead") - - case method do - :put -> binary_key_put(store, args) - :get -> binary_key_get(store, args) - :delete -> binary_key_delete(store, args) - :all -> binary_key_all(store, args) - end - end - end - - # TODO: Remove by 1.1.0 - defp has_binary_keys?(store) when store in [EtsCache, MnesiaCache], do: false - defp has_binary_keys?(store) do - {:module, ^store} = Code.ensure_loaded(store) - - not function_exported?(store, :all, 2) - end - - # TODO: Remove by 1.1.0 - defp binary_key_put(store, [backend_config, record_or_records]) do - record_or_records - |> List.wrap() - |> Enum.each(fn {key, value} -> - key = binary_key(key) - - store.put(backend_config, key, value) - end) - end - - # TODO: Remove by 1.1.0 - defp binary_key_get(store, [backend_config, key]) do - key = binary_key(key) - - store.get(backend_config, key) - end - - # TODO: Remove by 1.1.0 - defp binary_key_delete(store, [backend_config, key]) do - key = binary_key(key) - - store.delete(backend_config, key) - end - - # TODO: Remove by 1.1.0 - defp binary_key_all(store, [backend_config, match_spec]) do - match_spec = :ets.match_spec_compile([{match_spec, [], [:"$_"]}]) - - backend_config - |> store.keys() - |> Enum.map(&:erlang.binary_to_term/1) - |> :ets.match_spec_run(match_spec) - |> Enum.map(&{&1, binary_key_get(store, [backend_config, &1])}) - end - - # TODO: Remove by 1.1.0 - defp binary_key(key) do - key - |> List.wrap() - |> :erlang.term_to_binary() - end - - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use `put/3` instead" - def put(config, backend_config, key, value) do - put(config, backend_config, {key, value}) - end - - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use `all/2` instead" - def keys(config, backend_config) do - store(config).keys(backend_config) - end end diff --git a/lib/pow/store/credentials_cache.ex b/lib/pow/store/credentials_cache.ex index 933c3c10..4e618f43 100644 --- a/lib/pow/store/credentials_cache.ex +++ b/lib/pow/store/credentials_cache.ex @@ -36,14 +36,11 @@ defmodule Pow.Store.CredentialsCache do List all existing sessions for the user fetched from the backend store. """ @spec sessions(Config.t(), map()) :: [binary()] - def sessions(config, user), do: fetch_sessions(config, backend_config(config), user) - - # TODO: Refactor by 1.1.0 - defp fetch_sessions(config, backend_config, user) do + def sessions(config, user) do {struct, id} = user_to_struct_id!(user, []) config - |> Base.all(backend_config, [struct, :user, id, :session, :_]) + |> Base.all(backend_config(config), [struct, :user, id, :session, :_]) |> Enum.map(fn {[^struct, :user, ^id, :session, session_id], _value} -> session_id end) @@ -102,10 +99,6 @@ defmodule Pow.Store.CredentialsCache do Base.delete(config, backend_config, session_id) Base.delete(config, backend_config, session_key) - # TODO: Remove by 1.1.0 - {user, _metadata} when is_map(user) -> - Base.delete(config, backend_config, session_id) - :not_found -> :ok end @@ -122,10 +115,6 @@ defmodule Pow.Store.CredentialsCache do with {user_key, metadata} when is_list(user_key) <- Base.get(config, backend_config, session_id), user when is_map(user) <- Base.get(config, backend_config, user_key) do {user, metadata} - else - # TODO: Remove by 1.1.0 - {user, metadata} when is_map(user) -> {user, metadata} - :not_found -> :not_found end end @@ -174,20 +163,4 @@ defmodule Pow.Store.CredentialsCache do @spec raise_error(binary()) :: no_return() defp raise_error(message), do: raise message - - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use `users/2` or `sessions/2` instead" - def user_session_keys(config, backend_config, struct) do - config - |> Base.all(backend_config, [struct, :user, :_, :session, :_]) - |> Enum.map(fn {key, _value} -> - key - end) - end - - # TODO: Remove by 1.1.0 - @doc false - @deprecated "Use `sessions/2` instead" - def sessions(config, backend_config, user), do: fetch_sessions(config, backend_config, user) end diff --git a/mix.exs b/mix.exs index e0226e0a..36497565 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,7 @@ defmodule Pow.MixProject do [ app: :pow, version: @version, - elixir: "~> 1.6", + elixir: "~> 1.7", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, compilers: [:phoenix] ++ Mix.compilers(), @@ -35,8 +35,8 @@ defmodule Pow.MixProject do defp deps do [ - {:ecto, "~> 2.2 or ~> 3.0"}, - {:phoenix, "~> 1.3.0 or ~> 1.4.0"}, + {:ecto, "~> 3.0"}, + {:phoenix, "~> 1.4.7"}, {:phoenix_html, ">= 2.0.0 and <= 3.0.0"}, {:plug, ">= 1.5.0 and < 2.0.0", optional: true}, diff --git a/mix.lock b/mix.lock index d5186a8c..c3dfe7af 100644 --- a/mix.lock +++ b/mix.lock @@ -3,25 +3,25 @@ "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"}, "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"}, - "credo": {:hex, :credo, "1.2.2", "f57faf60e0a12b0ba9fd4bad07966057fde162b33496c509b95b027993494aab", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8f2623cd8c895a6f4a55ef10f3fdf6a55a9ca7bef09676bd835551687bf8a740"}, + "credo": {:hex, :credo, "1.2.3", "fbd11a840e45dc42e650401e16acd2a39dde080240b974e146cc65e81ada1cd3", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "3c07d51ff43748bb959752ede70e40557cdcb7d61615e47ae1e9e108e0b1ceec"}, "db_connection": {:hex, :db_connection, "2.2.1", "caee17725495f5129cb7faebde001dc4406796f12a62b8949f4ac69315080566", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "2b02ece62d9f983fcd40954e443b7d9e6589664380e5546b2b9b523cd0fb59e1"}, "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, - "ecto": {:hex, :ecto, "3.3.2", "002aa428c752a8ee4bb65baa9d1041f0514d7435d2e21d58cb6aa69a1076721d", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "c651e3ea0919241da314f518404b84449c8569525c4d4cf0f3d16f1d5d0a560c"}, - "ecto_sql": {:hex, :ecto_sql, "3.3.3", "7d8962d39f16181c1df1bbd0f64aa392bd9ce0b9f8ff5ff21d43dca3d624eee7", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3d5b8e14836d930448d79ab6ac325501c3c62caed83c34791d5af77718766795"}, + "ecto": {:hex, :ecto, "3.3.4", "95b05c82ae91361475e5491c9f3ac47632f940b3f92ae3988ac1aad04989c5bb", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "9b96cbb83a94713731461ea48521b178b0e3863d310a39a3948c807266eebd69"}, + "ecto_sql": {:hex, :ecto_sql, "3.3.4", "aa18af12eb875fbcda2f75e608b3bd534ebf020fc4f6448e4672fcdcbb081244", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5eccbdbf92e3c6f213007a82d5dbba4cd9bb659d1a21331f89f408e4c0efd7a8"}, "ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0db1ee8d1547ab4877c5b5dffc6604ef9454e189928d5ba8967d4a58a801f161"}, "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"}, "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "a10c6eb62cca416019663129699769f0c2ccf39428b3bb3c0cb38c718a0c186d"}, "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "d4b316c7222a85bbaa2fd7c6e90e37e953257ad196dc229505137c5e505e9eff"}, "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"}, "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"}, - "phoenix": {:hex, :phoenix, "1.4.13", "67271ad69b51f3719354604f4a3f968f83aa61c19199343656c9caee057ff3b8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ab765a0feddb81fc62e2116c827b5f068df85159c162bee760745276ad7ddc1b"}, + "phoenix": {:hex, :phoenix, "1.4.15", "5c39c330f46a33d752c6feceb25629ee8e62a158b997fea62bca59a59b28e3ea", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fd649bf11a76e42366096dc25737f89e8d4adedcbba6e969ae97f349f3cde5e7"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"}, "phoenix_html": {:hex, :phoenix_html, "2.14.0", "d8c6bc28acc8e65f8ea0080ee05aa13d912c8758699283b8d3427b655aabe284", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "b0bb30eda478a06dbfbe96728061a93833db3861a49ccb516f839ecb08493fbb"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm", "1f13f9f0f3e769a667a6b6828d29dec37497a082d195cc52dbef401a9b69bf38"}, "plug": {:hex, :plug, "1.9.0", "8d7c4e26962283ff9f8f3347bd73838e2413fbc38b7bb5467d5924f68f3a5a4a", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "9902eda2c52ada2a096434682e99a2493f5d06a94d6ac6bcfff9805f952350f1"}, "plug_cowboy": {:hex, :plug_cowboy, "2.1.2", "8b0addb5908c5238fac38e442e81b6fcd32788eaa03246b4d55d147c47c5805e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "7d722581ce865a237e14da6d946f92704101740a256bd13ec91e63c0b122fc70"}, - "plug_crypto": {:hex, :plug_crypto, "1.1.0", "854843d59062bf104ffe48fd92ad25a26fa3cc47558a13dd14c3025dc987542e", [:mix], [], "hexpm", "aef0a3aef2e4cda1c50b33cb25a56575117c9b25caaec8a7eb2dd7ce6ce13de5"}, + "plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"}, "postgrex": {:hex, :postgrex, "0.15.3", "5806baa8a19a68c4d07c7a624ccdb9b57e89cbc573f1b98099e3741214746ae4", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "4737ce62a31747b4c63c12b20c62307e51bb4fcd730ca0c32c280991e0606c90"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"}, diff --git a/test/extensions/persistent_session/plug/cookie_test.exs b/test/extensions/persistent_session/plug/cookie_test.exs index 68727be8..235a0fbc 100644 --- a/test/extensions/persistent_session/plug/cookie_test.exs +++ b/test/extensions/persistent_session/plug/cookie_test.exs @@ -24,7 +24,7 @@ defmodule PowPersistentSession.Plug.CookieTest do test "call/2 sets pow_persistent_session plug in conn", %{conn: conn, config: config} do conn = run_plug(conn) - expected_config = [mod: Session, plug: Session] ++ config + expected_config = [plug: Session] ++ config assert {Cookie, ^expected_config} = conn.private[:pow_persistent_session] refute conn.resp_cookies[@cookie_key] @@ -221,39 +221,6 @@ defmodule PowPersistentSession.Plug.CookieTest do end end - # TODO: Remove by 1.1.0 - test "call/2 is backwards-compatible with just user fetch clause", %{conn: conn, ets: ets} do - user = %User{id: 1} - id = store_in_cache(conn, "test", [id: user.id], backend: ets) - conn = - conn - |> persistent_cookie(@cookie_key, id) - |> run_plug() - - assert Plug.current_user(conn) == user - assert %{value: new_id, max_age: @max_age, path: "/"} = conn.resp_cookies[@cookie_key] - refute new_id == id - assert get_from_cache(conn, id, backend: ets) == :not_found - assert get_from_cache(conn, new_id, backend: ets) == {[id: 1], []} - end - - # TODO: Remove by 1.1.0 - test "call/2 is backwards-compatible with `:session_fingerprint` metadata", %{conn: conn, ets: ets} do - user = %User{id: 1} - id = store_in_cache(conn, "test", {[id: user.id], session_fingerprint: "fingerprint"}, backend: ets) - conn = - conn - |> persistent_cookie(@cookie_key, id) - |> run_plug() - - assert Plug.current_user(conn) == user - assert %{value: new_id, max_age: @max_age, path: "/"} = conn.resp_cookies[@cookie_key] - refute new_id == id - assert get_from_cache(conn, id, backend: ets) == :not_found - assert get_from_cache(conn, new_id, backend: ets) == {[id: 1], session_metadata: [fingerprint: "fingerprint"]} - assert conn.private[:pow_session_metadata][:fingerprint] == "fingerprint" - end - test "create/3 with custom TTL", %{conn: conn, config: config} do config = Keyword.put(config, :persistent_session_ttl, 1000) conn = diff --git a/test/mix/tasks/ecto/pow.ecto.install_test.exs b/test/mix/tasks/ecto/pow.ecto.install_test.exs index 5d681fff..48f61617 100644 --- a/test/mix/tasks/ecto/pow.ecto.install_test.exs +++ b/test/mix/tasks/ecto/pow.ecto.install_test.exs @@ -90,7 +90,7 @@ defmodule Mix.Tasks.Pow.Ecto.InstallTest do Mix.Project.in_project(:missing_top_level_ecto_dep, ".", fn _ -> # Insurance that we do test for top level ecto inclusion - assert Enum.any?(deps(), fn + assert Enum.any?(Mix.Dep.load_on_environment([]), fn %{app: :ecto_sql} -> true _ -> false end), "Ecto not loaded by dependency" @@ -118,12 +118,4 @@ defmodule Mix.Tasks.Pow.Ecto.InstallTest do end) end end - - # TODO: Refactor to just use Elixir 1.7 or higher by Pow 1.1.0 - defp deps do - case Kernel.function_exported?(Mix.Dep, :load_on_environment, 1) do - true -> apply(Mix.Dep, :load_on_environment, [[]]) - false -> apply(Mix.Dep, :loaded, [[]]) - end - end end diff --git a/test/mix/tasks/phoenix/pow.phoenix.install_test.exs b/test/mix/tasks/phoenix/pow.phoenix.install_test.exs index fa48a801..44a04352 100644 --- a/test/mix/tasks/phoenix/pow.phoenix.install_test.exs +++ b/test/mix/tasks/phoenix/pow.phoenix.install_test.exs @@ -106,7 +106,7 @@ defmodule Mix.Tasks.Pow.Phoenix.InstallTest do Mix.Project.in_project(:missing_top_level_phoenix_dep, ".", fn _ -> # Insurance that we do test for top level phoenix inclusion - assert Enum.any?(deps(), fn + assert Enum.any?(Mix.Dep.load_on_environment([]), fn %{app: :phoenix} -> true _ -> false end), "Phoenix not loaded by dependency" @@ -207,12 +207,4 @@ defmodule Mix.Tasks.Pow.Phoenix.InstallTest do end) end) end - - # TODO: Refactor to just use Elixir 1.7 or higher by Pow 1.1.0 - defp deps do - case Kernel.function_exported?(Mix.Dep, :load_on_environment, 1) do - true -> apply(Mix.Dep, :load_on_environment, [[]]) - false -> apply(Mix.Dep, :loaded, [[]]) - end - end end diff --git a/test/pow/ecto/schema/changeset_test.exs b/test/pow/ecto/schema/changeset_test.exs index 13c88a62..878ed5cb 100644 --- a/test/pow/ecto/schema/changeset_test.exs +++ b/test/pow/ecto/schema/changeset_test.exs @@ -184,24 +184,6 @@ defmodule Pow.Ecto.Schema.ChangesetTest do assert changeset.errors[:password_hash] == {"can't be blank", [validation: :required]} end - # TODO: Remove by 1.1.0 - test "handle `confirm_password` conversion" do - params = - @valid_params - |> Map.delete("password_confirmation") - |> Map.put("confirm_password", "secret1234") - changeset = User.changeset(%User{}, params) - - assert changeset.valid? - - params = Map.put(params, "confirm_password", "invalid") - changeset = User.changeset(%User{}, params) - - refute changeset.valid? - assert changeset.errors[:confirm_password] == {"does not match confirmation", [validation: :confirmation]} - refute changeset.errors[:password_confirmation] - end - test "can use custom password hash methods" do password_hash = &(&1 <> "123") password_verify = &(&1 == &2 <> "123") diff --git a/test/pow/phoenix/html/form_template_test.exs b/test/pow/phoenix/html/form_template_test.exs index d69e9f1c..76707dc1 100644 --- a/test/pow/phoenix/html/form_template_test.exs +++ b/test/pow/phoenix/html/form_template_test.exs @@ -19,20 +19,4 @@ defmodule Pow.Phoenix.HTML.FormTemplateTest do assert html =~ "<%= password_input f, :password %>" assert html =~ "<%= error_tag f, :password %>" end - - test "render/2 with bootstrap" do - html = FormTemplate.render([ - {:text, {:changeset, :pow_user_id_field}}, - {:password, :password}, - {:password, :password_confirmation} - ], bootstrap: true) - - assert html =~ "
" - assert html =~ "<%= label f, Pow.Ecto.Schema.user_id_field(@changeset), class: \"control-label\" %>" - assert html =~ "<%= text_input f, Pow.Ecto.Schema.user_id_field(@changeset), class: \"form-control\" %>" - assert html =~ "<%= error_tag f, Pow.Ecto.Schema.user_id_field(@changeset) %>" - assert html =~ "<%= label f, :password, class: \"control-label\" %>" - assert html =~ "<%= password_input f, :password, class: \"form-control\" %>" - assert html =~ "<%= error_tag f, :password %>" - end end diff --git a/test/pow/plug/session_test.exs b/test/pow/plug/session_test.exs index d0b89cea..e3087621 100644 --- a/test/pow/plug/session_test.exs +++ b/test/pow/plug/session_test.exs @@ -26,7 +26,7 @@ defmodule Pow.Plug.SessionTest do test "call/2 sets plug in :pow_config", %{conn: conn} do conn = run_plug(conn) - expected_config = [mod: Session, plug: Session] ++ @default_opts + expected_config = [plug: Session] ++ @default_opts assert is_nil(conn.assigns[:current_user]) assert conn.private[:pow_config] == expected_config @@ -230,29 +230,6 @@ defmodule Pow.Plug.SessionTest do assert conn.assigns[:current_user] == @user end - # TODO: Remove by 1.1.0 - test "backwards compatible", %{conn: conn} do - ttl = 100 - config = Keyword.put(@default_opts, :session_ttl_renewal, ttl) - stale_timestamp = :os.system_time(:millisecond) - ttl - 1 - session_id = sign_token("token") - - @store_config - |> Keyword.put(:namespace, "credentials") - |> EtsCacheMock.put({"token", {@user, stale_timestamp}}) - - conn = - conn - |> Conn.fetch_session() - |> Conn.put_session(config[:session_key], session_id) - |> run_plug(config) - - assert new_session_id = get_session_id(conn) - assert new_session_id != session_id - - assert conn.assigns[:current_user] == @user - end - describe "create/2" do test "deletes existing and creates new session id", %{conn: new_conn} do conn = diff --git a/test/pow/store/backend/ets_cache_test.exs b/test/pow/store/backend/ets_cache_test.exs index 07113ba0..a282f1dc 100644 --- a/test/pow/store/backend/ets_cache_test.exs +++ b/test/pow/store/backend/ets_cache_test.exs @@ -66,11 +66,4 @@ defmodule Pow.Store.Backend.EtsCacheTest do assert EtsCache.get(config, "key1") == :not_found assert EtsCache.get(config, "key2") == :not_found end - - # TODO: Remove by 1.1.0 - test "backwards compatible" do - assert EtsCache.put(@default_config, "key", "value") == :ok - :timer.sleep(50) - assert EtsCache.keys(@default_config) == [{"key", "value"}] - end end diff --git a/test/pow/store/backend/mnesia_cache_test.exs b/test/pow/store/backend/mnesia_cache_test.exs index 07087e8b..84d14ec9 100644 --- a/test/pow/store/backend/mnesia_cache_test.exs +++ b/test/pow/store/backend/mnesia_cache_test.exs @@ -112,13 +112,6 @@ defmodule Pow.Store.Backend.MnesiaCacheTest do :timer.sleep(50) assert MnesiaCache.get(config, "key") == :not_found end - - # TODO: Remove by 1.1.0 - test "backwards compatible" do - assert MnesiaCache.put(@default_config, "key", "value") == :ok - :timer.sleep(50) - assert MnesiaCache.keys(@default_config) == [{"key", "value"}] - end end defp start(config) do @@ -334,33 +327,4 @@ defmodule Pow.Store.Backend.MnesiaCacheTest do true = :rpc.call(node_a, Node, :connect, [node_b]) :timer.sleep(500) end - - # TODO: Remove by 1.1.0 - describe "backwards compatible" do - setup do - :mnesia.kill() - - File.rm_rf!("tmp/mnesia") - File.mkdir_p!("tmp/mnesia") - - :ok - end - - test "removes old entries" do - :ok = :mnesia.start() - {:atomic, :ok} = :mnesia.change_table_copy_type(:schema, node(), :disc_copies) - {:atomic, :ok} = :mnesia.create_table(MnesiaCache, type: :set, disc_copies: [node()]) - :ok = :mnesia.wait_for_tables([MnesiaCache], :timer.seconds(15)) - - key = "#{@default_config[:namespace]}:key1" - - :ok = :mnesia.dirty_write({MnesiaCache, key, {"key1", "test", @default_config, :os.system_time(:millisecond) + 100}}) - - :stopped = :mnesia.stop() - - start(@default_config) - - assert :mnesia.dirty_read({MnesiaCache, key}) == [] - end - end end diff --git a/test/pow/store/base_test.exs b/test/pow/store/base_test.exs index 80f9e6e6..4353eaae 100644 --- a/test/pow/store/base_test.exs +++ b/test/pow/store/base_test.exs @@ -74,23 +74,4 @@ defmodule Pow.Store.BaseTest do "#{Pow.Config.get(config, :namespace, "cache")}:#{key}" end end - - # TODO: Remove by 1.1.0 - test "backwards compatible with binary keys support" do - config = [backend: BackwardsCompabilityMock] - - assert BaseMock.put(config, [BackwardsCompabilityMock, :id, 2], :value) == :ok - binary_key = "default_namespace:#{:erlang.term_to_binary([BackwardsCompabilityMock, :id, 2])}" - assert_received {:put, ^binary_key, :value} - - assert BaseMock.get(config, [BackwardsCompabilityMock, :id, 2]) == :value - assert_received {:get, ^binary_key} - - assert BaseMock.delete(config, [BackwardsCompabilityMock, :id, 2]) == :ok - assert_received {:delete, ^binary_key} - - assert BaseMock.all(config, [BackwardsCompabilityMock | :_]) == [{[BackwardsCompabilityMock, :id, 2], :value}] - assert BaseMock.all(config, [BackwardsCompabilityMock, :id, :_]) == [{[BackwardsCompabilityMock, :id, 2], :value}] - assert BaseMock.all(config, [BackwardsCompabilityMock, :id, 3]) == [] - end end diff --git a/test/pow/store/credentials_cache_test.exs b/test/pow/store/credentials_cache_test.exs index 58cab840..a429e1f1 100644 --- a/test/pow/store/credentials_cache_test.exs +++ b/test/pow/store/credentials_cache_test.exs @@ -96,29 +96,6 @@ defmodule Pow.Store.CredentialsCacheTest do assert ets.get(@backend_config, [CompositePrimaryFieldsUser, :user, [another_id: 2, some_id: 1]]) == user end - # TODO: Remove by 1.1.0 - test "backwards compatible", %{ets: ets} do - user_1 = %User{id: 1} - timestamp = :os.system_time(:millisecond) - - ets.put(@backend_config, {"key_1", {user_1, inserted_at: timestamp}}) - - assert CredentialsCache.get(@config, @backend_config, "key_1") == {user_1, inserted_at: timestamp} - assert CredentialsCache.delete(@config, @backend_config, "key_1") == :ok - assert CredentialsCache.get(@config, @backend_config, "key_1") == :not_found - - assert CredentialsCache.user_session_keys(@config, @backend_config, User) == [] - - user_2 = %UsernameUser{id: 1} - - CredentialsCache.put(@config, @backend_config, "key_1", {user_1, a: 1}) - CredentialsCache.put(@config, @backend_config, "key_2", {user_1, a: 1}) - CredentialsCache.put(@config, @backend_config, "key_3", {user_2, a: 1}) - - assert CredentialsCache.user_session_keys(@config, @backend_config, User) == [[Pow.Test.Ecto.Users.User, :user, 1, :session, "key_1"], [Pow.Test.Ecto.Users.User, :user, 1, :session, "key_2"]] - assert CredentialsCache.user_session_keys(@config, @backend_config, UsernameUser) == [[Pow.Test.Ecto.Users.UsernameUser, :user, 1, :session, "key_3"]] - end - describe "with EtsCache backend" do setup do start_supervised!({EtsCache, []})