Giter Site home page Giter Site logo

wintercms / wn-user-plugin Goto Github PK

View Code? Open in Web Editor NEW
10.0 6.0 19.0 892 KB

Front-end User plugin for Winter CMS

Home Page: https://wintercms.com/

License: MIT License

PHP 90.93% JavaScript 0.72% HTML 8.36%
wintercms plugin user authentication hacktoberfest

wn-user-plugin's Introduction

Front-end user plugin

Build Status MIT License

Front-end user management for Winter CMS.

Requirements

This plugin requires either Snowboard framework or the original Ajax Framework to be included in your layout or page in order to handle form requests.

Managing users

Users are managed on the Users tab found in the back-end. Each user provides minimal data fields - Name, Surname, Email and Password. The Name can represent either the person's first name or their full name, making the Surname field optional, depending on the complexity of your site.

Below the Email field is an checkbox to block all outgoing mail sent to the user. This is a useful feature for accounts with an email address that is bouncing mail or has reported spam. When checked, no mail will ever be sent to this address, except for the mail template used for resetting the password.

Plugin settings

This plugin creates a Settings menu item, found by navigating to Settings > Users > User settings. This page allows the setting of common features, described in more detail below.

Registration

Registration to the site is allowed by default. If you are running a closed site, or need to temporarily disable registration, you may disable this feature by switching Allow user registration to the OFF setting.

Activation

Activation is a process of vetting a user who joins the site. By default, users are activated automatically when they register and an activated account is required to sign in.

The Activation mode specifies the activation workflow:

  • Automatic: This mode will automatically activate a user when they first register. This is the same as disabling activation entirely and is the default setting.
  • User: The user can activate their account by responding to a confirmation message sent to their nominated email address.
  • Administrator: The user can only be activated by an administrator via the back-end area.

You can allow users to sign in without activating by switching Sign in requires activation to the OFF setting. This is useful for minimising friction when registering, however with this approach it is often a good idea to disable any "identity sensitive" features until the user has been activated, such as posting content. Alternatively, you could implement a grace period that deletes users (with sufficient warning!) who have not activated within a given period of time.

Users have the ability to resend the activation email by clicking Send the verification email again found in the Account component.

Sign in

By default a User will sign in to the site using their email address as a unique identifier. You may use a unique login name instead by changing the Login attribute value to Username. This will introduce a new field called Username for each user, allowing them to specify their own short name or alias for identification. Both the Email address and Username must be unique to the user.

If a user experiences too many failed sign in attempts, their account will be temporarily suspended for a period of time. This feature is enabled by default and will suspend an account for 15 minutes after 5 failed sign in attempts, for a given IP address. You may disable this feature by switching Throttle attempts to the OFF setting.

As a security precaution, you may restrict users from having sessions across multiple devices at the same time. Enable the Prevent concurrent sessions setting to use this feature. When a user signs in to their account, it will automatically sign out the user for all other sessions.

Notifications

When a user is first activated -- either by registration, email confirmation or administrator approval -- they are sent a welcome email. To disable the welcome email, select "Do not send a notification" from the Welcome mail template dropdown. The default message template used is winter.user::mail.welcome and you can customize this by selecting Mail > Mail Templates from the settings menu.

Session component

The session component should be added to a layout that has registered users. It has no default markup.

User variable

You can check the logged in user by accessing the {{ user }} Twig variable:

{% if user %}
    <p>Hello {{ user.name }}</p>
{% else %}
    <p>Nobody is logged in</p>
{% endif %}

Signing out

The Session component allows a user to sign out of their session.

<a href="javascript:;" data-request="onLogout" data-request-data="redirect: '/good-bye'">Sign out</a>

Page restriction

The Session component allows the restriction of a page or layout by allowing only signed in users, only guests or no restriction. This example shows how to restrict a page to users only:

title = "Restricted page"
url = "/users-only"

[session]
security = "user"
redirect = "home"

The security property can be user, guest or all. The redirect property refers to a page name to redirect to when access is restricted.

Route restriction

Access to routes can be restricted by applying the AuthMiddleware.

Route::group(['middleware' => 'Winter\User\Classes\AuthMiddleware'], function () {
    // All routes here will require authentication
});

Account component

The account component provides a user sign in form, registration form, activation form and update form. To display the form:

title = "Account"
url = "/account/:code?"

[account]
redirect = "home"
paramCode = "code"
==
{% component 'account' %}

If the user is logged out, this will display a sign in and registration form. Otherwise, it will display an update form. The redirect property is the page name to redirect to after the submit process is complete. The paramCode is the URL routing code used for activating the user, only used if the feature is enabled.

Reset Password component

The reset password component allows a user to reset their password if they have forgotten it.

title = "Forgotten your password?"
url = "/forgot-password/:code?"

[resetPassword]
paramCode = "code"
==
{% component 'resetPassword' %}

This will display the initial restoration request form and also the password reset form used after the verification email has been received by the user. The paramCode is the URL routing code used for resetting the password.

Using a login name

By default the User plugin will use the email address as the login name. To switch to using a user defined login name, navigate to the backend under System > Users > User Settings and change the Login attribute under the Sign in tab to be Username. Then simply ask for a username upon registration by adding the username field:

<form data-request="onRegister">
    <label>Full Name</label>
    <input name="name" type="text" placeholder="Enter your full name">

    <label>Email</label>
    <input name="email" type="email" placeholder="Enter your email">

    <label>Username</label>
    <input name="username" placeholder="Pick a login name">

    <label>Password</label>
    <input name="password" type="password" placeholder="Choose a password">

    <button type="submit">Register</button>
</form>

We can add any other additional fields here too, such as phone, company, etc.

Password length requirements

By default, the User plugin requires a minimum password length of 8 characters for all users when registering or changing their password. You can change this length requirement by going to config/config.php. Inside the file, change the value of the minPasswordLength parameter to your own.

Error handling

Flash messages

This plugin makes use of Winter CMS's Flash API. In order to display the error messages, you need to place the following snippet in your layout or page.

{% flash %}
    <div class="alert alert-{{ type == 'error' ? 'danger' : type }}">{{ message }}</div>
{% endflash %}

AJAX errors

The User plugin displays AJAX error messages in a simple alert()-box by default. However, this might scare non-technical users. You can change the default behavior of an AJAX error from displaying an alert() message, like this:

<script>
    $(window).on('ajaxErrorMessage', function (event, message) {

        // This can be any custom JavaScript you want
        alert('Something bad happened, mate, here it is: ' + message);

        // This will stop the default alert() message
        event.preventDefault();

    })
</script>

Checking if a login name is already taken

NOTE: Implementing the below example may be a privacy risk as it allows unauthenticated users to query your service to see if given email addresses are signed up to your service or not.

Here is a simple example of how you can quickly check if an email address / username is available in your registration forms. First, inside the page code, define the following AJAX handler to check the login name, here we are using the email address:

public function onCheckEmail()
{
    return ['isTaken' => Auth::findUserByLogin(post('email')) ? 1 : 0];
}

For the email input we use the data-request and data-track-input attributes to call the onCheckEmail handler any time the field is updated. The data-request-success attribute will call some jQuery code to toggle the alert box.

<div class="form-group">
    <label>Email address</label>
    <input
        name="email"
        type="email"
        class="form-control"
        data-request="onCheckEmail"
        data-request-success="$('#loginTaken').toggle(!!data.isTaken)"
        data-track-input />
</div>

<div id="loginTaken" class="alert alert-danger" style="display: none">
    Sorry, that login name is already taken.
</div>

Overriding functionality

Here is how you would override the onSignin() handler to log any error messages. Inside the page code, define this method:

function onSignin()
{
    try {
        return $this->account->onSignin();
    }
    catch (Exception $ex) {
        Log::error($ex);
    }
}

Here the local handler method will take priority over the account component's event handler. Then we simply inherit the logic by calling the parent handler manually, via the component object ($this->account).

Auth facade

There is an Auth facade you may use for common tasks, it primarily inherits the Winter\Storm\Auth\Manager class for functionality.

You may use Auth::register to register an account:

$user = Auth::register([
    'name' => 'Some User',
    'email' => '[email protected]',
    'password' => 'changeme',
    'password_confirmation' => 'changeme',
]);

The second argument can specify if the account should be automatically activated:

// Auto activate this user
$user = Auth::register([...], true);

The Auth::check method is a quick way to check if the user is signed in.

// Returns true if signed in.
$loggedIn = Auth::check();

To return the user model that is signed in, use Auth::getUser instead.

// Returns the signed in user
$user = Auth::getUser();

You may authenticate a user by providing their login and password with Auth::authenticate.

// Authenticate user by credentials
$user = Auth::authenticate([
    'login' => post('login'),
    'password' => post('password')
]);

The second argument is used to store a non-expire cookie for the user.

$user = Auth::authenticate([...], true);

You can also authenticate as a user simply by passing the user model along with Auth::login.

// Sign in as a specific user
Auth::login($user);

The second argument is the same.

// Sign in and remember the user
Auth::login($user, true);

You may look up a user by their login name using the Auth::findUserByLogin method.

$user = Auth::findUserByLogin('[email protected]');

Guest users

Creating a guest user allows the registration process to be deferred. For example, making a purchase without needing to register first. Guest users are not able to sign in and will be added to the user group with the code guest.

Use the Auth::registerGuest method to create a guest user, it will return a user object and can be called multiple times. The unique identifier is the email address, which is a required field.

$user = Auth::registerGuest(['email' => '[email protected]']);

When a user registers with the same email address using the Auth::register method, they will inherit the existing guest user account.

// This will not throw an "Email already taken" error
$user = Auth::register([
    'email' => '[email protected]',
    'password' => 'changeme',
    'password_confirmation' => 'changeme',
]);

Important: If you are using guest accounts, it is important to disable sensitive functionality for user accounts that are not verified, since it may be possible for anyone to inherit a guest account.

You may also convert a guest to a registered user with the convertToRegistered method. This will generate a random password and sends an invitation using the winter.user::mail.invite template.

$user->convertToRegistered();

To disable the notification and password reset, pass the first argument as false.

$user->convertToRegistered(false);

Events

This plugin will fire some global events that can be useful for interacting with other plugins.

  • winter.user.beforeRegister: Before the user's registration is processed. Passed the $data variable by reference to enable direct modifications to the $data provided to the Auth::register() method.
  • winter.user.register: The user has successfully registered. Passed the $user object and the submitted $data variable.
  • winter.user.beforeAuthenticate: Before the user is attempting to authenticate using the Account component.
  • winter.user.login: The user has successfully signed in.
  • winter.user.logout: The user has successfully signed out.
  • winter.user.activate: The user has activated their own account by email validation.
  • winter.user.deactivate: The user has opted-out of the site by deactivating their account. This should be used to disable any content the user may want removed.
  • winter.user.reactivate: The user has reactivated their own account by signing back in. This should revive the users content on the site.
  • winter.user.getNotificationVars: Fires when sending a user notification to enable passing more variables to the email templates. Passes the $user model the template will be for.
  • winter.user.view.extendListToolbar: Fires when the user listing page's toolbar is rendered.
  • winter.user.view.extendPreviewToolbar: Fires when the user preview page's toolbar is rendered.

Here is an example of hooking an event:

Event::listen('winter.user.deactivate', function($user) {
    // Hide all posts by the user
});

A common requirement is to adapt another to a legacy authentication system. In the example below, the WordPressLogin::check method would check the user password using an alternative hashing method, and if successful, update to the new one used by Winter CMS.

Event::listen('winter.user.beforeAuthenticate', function($component, $credentials) {
    $login = array_get($credentials, 'login');
    $password = array_get($credentials, 'password');

    /*
     * No such user exists
     */
    if (!$user = Auth::findUserByLogin($login)) {
        return;
    }

    /*
     * The user is logging in with their old WordPress account
     * for the first time. Rehash their password using the new
     * Winter CMS system.
     */
    if (WordPressLogin::check($user->password, $password)) {
        $user->password = $user->password_confirmation = $password;
        $user->forceSave();
    }
});

wn-user-plugin's People

Contributors

acasar avatar aic-bv avatar alekseybobkov avatar alvaro-canepa avatar alxy avatar aurelien-roy avatar bennothommo avatar buuug7 avatar daftspunk avatar felixinx avatar gabsource avatar gergo85 avatar gpasztor87 avatar jakobfdev avatar jimcottrell avatar joakimbo avatar luketowers avatar mjauvin avatar romainmazb avatar rubenvanerk avatar scottbedard avatar shahiem avatar slowpokefarm avatar theservat avatar timfoerster avatar to-kn avatar tobias-kuendig avatar vojtasvoboda avatar webvpf avatar yapsr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

wn-user-plugin's Issues

Error when sending an invitation with Debugbar enabled

  • Plugin version: v2.0.0
  • Winter CMS Build: 203d2c4
  • PHP Version: 8.0.6
  • Plugins Installed: Flynsarmy.IdeHelper, Winter.Builder, Winter.Debugbar

When I choose the option to send an invitation I get the error:
Symfony\Component\Debug\Exception\FatalThrowableError: array_merge(): Argument #1 must be of type array, null given in /path/to/project/plugins/winter/user/models/User.php:498

Contents of $result on that line:

array:1 [
  0 => null
]

Class not found without Notify plugin installed

  • Plugin version: a60045b
  • Winter CMS Build: 203d2c4
  • PHP Version: 8.0.6
  • Plugins Installed: Flynsarmy.IdeHelper, Winter.Builder, Winter.Debugbar

I wanted to make use of the registerClassAliases function, so I updated to a60045b.
When composer runs the php artisan winter:version command, I'm getting the following error: Class "Winter\User\Classes\UserEventBase" not found

This error does not occur when I have the Notify plugin installed.

Password length?

Hi. The documentation says:

Password length requirements
By default, the User plugin requires a minimum password length of 8 characters for all users when registering or changing their password. You can change this length requirement by going to backend and navigating to System > Users > User Settings. Inside the Registration tab, a Minimum password length field is provided, allowing you to increase or decrease this limit to your preferred length.

There are no fields on the registration tab that set the minimum number. But minimun length i find in config.php.
User model has rules. And validation has nothing to do with the config

 'password' => 'required:create|between:8,255|confirmed',

image

fields.yaml in settings model:

# ===================================
#  Field Definitions
# ===================================

tabs:
    fields:
        # Throttle Sign In
        use_throttle:
            span: left
            label: winter.user::lang.settings.use_throttle
            comment: winter.user::lang.settings.use_throttle_comment
            type: switch
            tab: winter.user::lang.settings.signin_tab

        # Prevent concurrent sessions
        block_persistence:
            span: right
            label: winter.user::lang.settings.block_persistence
            comment: winter.user::lang.settings.block_persistence_comment
            type: switch
            tab: winter.user::lang.settings.signin_tab

        # Login Attribute
        login_attribute:
            span: left
            label: winter.user::lang.settings.login_attribute
            commentAbove: winter.user::lang.settings.login_attribute_comment
            type: radio
            tab: winter.user::lang.settings.signin_tab

        # Remeber Login Mode
        remember_login:
            span: right
            label: winter.user::lang.settings.remember_login
            commentAbove: winter.user::lang.settings.remember_login_comment
            type: radio
            tab: winter.user::lang.settings.signin_tab

        # Require Activation
        allow_registration:
            span: left
            label: winter.user::lang.settings.allow_registration
            comment: winter.user::lang.settings.allow_registration_comment
            type: switch
            tab: winter.user::lang.settings.registration_tab

        # Enable registration throttling
        use_register_throttle:
            span: right
            label: winter.user::lang.settings.use_register_throttle
            comment: winter.user::lang.settings.use_register_throttle_comment
            type: switch
            tab: winter.user::lang.settings.registration_tab

        # Require Activation
        require_activation:
            span: left
            label: winter.user::lang.settings.require_activation
            comment: winter.user::lang.settings.require_activation_comment
            type: switch
            tab: winter.user::lang.settings.activation_tab

        # Activation Mode
        activate_mode:
            span: left
            commentAbove: winter.user::lang.settings.activate_mode_comment
            label: winter.user::lang.settings.activate_mode
            type: radio
            tab: winter.user::lang.settings.activation_tab

User modal also has method :

public static function getMinPasswordLength()
    {
        return Config::get('winter.user::minPasswordLength', 8);
    }

use only on reset password and signin.

image

upd:
Sorry I didn't see beforeValidate method where change rules with minLength from config.
But its not work beacuse validation rules in onRegister method in Account component take rules from property, before beforeValidate.

image

If I'm wrong, I apologize

Downgrading instead of Upgrading

I start the update

composer update

Result

2023-08-09_164852

Then

php artisan winter:up

Result

1

2

Then

composer update

Result

3

Then

php artisan winter:up

Result

4

So it repeats endlessly.
This came up a few months ago.

Unactivated users get wrong error message

Hello,

My users have to activate their account before they can sign in. (Activation type: User)
This is to protect guest users from evil people 'stealing' their accounts. (my way of handling the important text block)

When a user tries to log in, that didn't activate his account yet, they get an error message like
The details you entered did not match our records. Please double-check and try again.

This error is wrong, and it should be something like
You didn't activate your account yet. Please check your email and activate your account.
and, ideally, resend the activation email.

User exists in backend user list, in database and with the onCheckEmail method. So I can change my password, but can never log in because I forgot to activate my account.

The current error message sends me in an infinite loop.

  1. It basically tells me to register
  2. But when registering it says my account already exists
  3. Which suggests me to reset my password
  4. Which when you do, you cannot log in because you are not activated yet
  5. Which resets the whole loop

Users are unable to fully delete their own account

The plugin currently provides no way for a user to fully delete their own account - the only option currently available is to deactivate the user's account via the Account component, which soft-deletes the user account but keeps it available in the system. The user account is still visible to administrators, and can be re-activated by simply logging in again.

Given the movement towards data privacy and the right to be forgotten that is happening across the world, it feels prudent that we allow people to delete their own accounts permanently if they wish.

This could perhaps be allowed as a setting, or via another action in the Account component.

Password confirmation doesn't match

Hey. Using latest versions of wintercms and plugins, getting this "Password confirmation doesn't match" in Sign Up form.
Debug $user in vendor/winter/storm/src/Auth/Manager.php at line 138 right after filling model shows that password already hashed but password_confirmation obviously not.

image

And btw, this validation error doesn't prevent user of being created.

Any workarounds?

EDIT:
Just debugged $user entity in beforeValidate() hook in plugins/winter/user/models/User.php at line 242, situation the same, password already hashed but password_confirmation not.

EDIT:
Had issue here, but still im courious about why i see validation error instead of actual in event handler.
Event::listen('winter.user.activate', function ($user) { ... });

Redefinition of variable parameters

Hello! In my plugin I'm using this code, to redefinition of variable parameters. On OctoberCMS it's working

public function boot(){
\RainLab\User\Models\User::extend(function($model) {
$model->bindEvent('model.beforeValidate', function() use ($model) {
$model->rules['email'] = 'required|between:55,60|email|unique:users';
});
});
}

On Winter.User when I replace \RainLab\User\Models\User to \Winter\User\Models\User it doesn`t work. Why?

Guest users are not working as expected

As discussed on Discord with @mjauvin, guest users are currently not working.

I create a guest (documentation)

$user = Auth::registerGuest(['email' => $email]);

Assuming onCheckEmail is not an official part of the plugin but just an example; this can be ignored
I adjusted example the onCheckEmail() function because Auth::findUserByLogin returns TRUE if a user is found regardless of its value user->is_guest. In my case (and everyone elses?), it should also return FALSE if user->is_guest because even though the user already exists, the user with that specific e-mailadress is still available to be claimed by anyone.
I adjusted the function to solve this issue (which is not really an issue but not documented I guess).

~~public function onCheckEmail() {

        // keep isTaken
        $isTaken = 1;

        // get email
        $email = post('email');

        // search the user
        $user = Auth::findUserByLogin($email);

        // if user is NOT found or user is guest -> account is still available to claim
        if (!$user || $user->is_guest) $isTaken = 0;

        return ['isTaken' => $isTaken];

    }~~

This brings me to the next issue;
When the guest user finally wants to register, he gets a validation error that the e-mailadress is already in use because:

plugins/winter/user/components/Account.php (around line 295)
$rules = (new UserModel)->rules;

Which contains

plugins/winter/user/models/User.php
public $rules = [
        'email'    => 'required|between:6,255|email|unique:users',
        ...
];

but this should not ever happen because even though the user already exists and the e-mail is already taken, the e-mail can still be used to register the guest and claim the account in the process.

Error after installing plugin

After installing via composer plugin User does not install other plugins via composer.
When trying to install a new plugin, an error appears in the console, and new plugin is not installed because of this. The error refers to User plugin.

SQL error after plugin upgrade

Hello!
I continue to replace the rainlab's plugins with winter.
Rainlab's user plugin version 1.64
I've removed the plugin's folder and installed the Winter user plugin.
This error is generated:
"SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'users' already exists (SQL: create table users (idint unsigned not null auto_increment primary key,namevarchar(255) null,emailvarchar(255) not null,passwordvarchar(255) not null,activation_codevarchar(255) null,persist_codevarchar(255) null,reset_password_codevarchar(255) null,permissionstext null,is_activatedtinyint(1) not null default '0',activated_attimestamp null,last_logintimestamp null,created_attimestamp null,updated_at timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci' engine = InnoDB)" on line 760 of C:\laragon\www\winter3\vendor\laravel\framework\src\Illuminate\Database\Connection.php

Activation e-mail: registering with an e-mail that does not exist

Users can register with e-mailadresses that don't exist.

For example:
I register using [email protected]l.
I get a green flash message saying an activation e-mail has been sent to the e-mailadress.

In my case, a guest account is created using that invalid e-mailadress.

Possible solution:
The green flash message shouldn't appear.
Since the e-mail failed, a red flash message should appear saying that the e-mailadress does not exist. A user should not be created.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.