Jump to content

Integrating a member / visitor login form


thetuningspoon

Recommended Posts

I have a tricky question with the roles.

i have those roles

-(guest)

-user

-editor

-admin

-superadmin

For security reasons I want to allow the password reset only for people that are "users" but have no higher role. (An editor should ask the superadmin instead.)

Is there an easy way to check whether a user has ONLY the role of "user", and nothing else?

Link to comment
Share on other sites

you mean like this?

if( $user->hasRole('user')       &&
    !$user->hasRole('editor')    &&
    !$user->hasRole('admin')     &&
    !$user->hasRole('superadmin'))
{
    //password reset stuff here
}
  • Like 2
Link to comment
Share on other sites

When you create a user through the admin or through the API, it always gets assigned the guest role along with the custom roles that you assign. So if you assign a custom role "user", your user will have 2 roles: guest and user.

Now you can check if a user has the role "user" and a total of 2 roles assigned. In PHP:

if (count($user->roles) == 2 && $user->hasRole("user")) {
    //do stuff here
}
  • Like 3
Link to comment
Share on other sites

if( $user->hasRole('user')       &&
    !$user->hasRole('editor')    &&
    !$user->hasRole('admin')     &&
    !$user->hasRole('superadmin'))
{
    //password reset stuff here
}

Thanks Macrura. That would work and that is how I had it. Problem is that this would break my security concept the minute I change a role or add one - and forget to update this code afterwards.

Now you can check if a user has the role "user" and a total of 2 roles assigned. In PHP:

if (count($user->roles) == 2 && $user->hasRole("user")) {

//do stuff here

}

gebeer, this is so brilliant!  - exactly what I was looking for. And you come up with ideas like this at 3 in the morning? :)
Link to comment
Share on other sites

  • 2 months later...
  • 1 month later...

Hi Guys, 

So I have implemented Ryan's template setup for login, profile,  logout and reset pass. 

My question is, what if we want to allow the user to login with the username or their email, which most forms do nowadays. 

Any advice on the approach. 

Essentially, 1 form, 2 input fields, 1 submit button. 

Here is the template being used to handle login as of now:

if($user->isLoggedin()) $session->redirect('/profile/'); 
if($input->post->username && $input->post->pass) {
  $username = $sanitizer->username($input->post->username); 
  $pass = $input->post->pass; 
  $u = $users->get($username); 
  if($u->id && $u->tmp_pass && $u->tmp_pass === $pass) {
    // user logging in with tmp_pass, so change it to be their real pass
    $u->of(false);
    $u->pass = $u->tmp_pass;
    $u->save();
    $u->of(true);
  }
  $u = $session->login($username, $pass); 
  if($u) {
    // user is logged in, get rid of tmp_pass
    $u->of(false);
    $u->tmp_pass = '';
    $u->save();
    // now redirect to the profile edit page
    $session->redirect('/profile/'); 
  }
}

// present the login form
$headline = $input->post->username ? "Login failed" : "Please login";
$page->body = "
  <h2>$headline</h2>
  <form action='./' method='post'>
  <p>
  <label>Username <input type='text' name='username'></label>
  <label>Password <input type='password' name='pass'></label>
  </p>
  <input type='submit'>
  </form>
  <p><a href='/reset-pass/'>Forgot your password?</a></p>
";

include("./main.php"); // main markup template
Link to comment
Share on other sites

Hey Adrian,

I saw that post before posting here but ended realizing that the member was asking to replace the username.

However, I want the user to either login using a username or their email. So basically, checking the input field for either one.

Similar to what the temp_pass does. However, still haven't slapped together a solution.

Thanks!

Link to comment
Share on other sites

Sorry about that - good point. I think the problem with what you're trying to implement is that @ is not allowed in name fields.

This is a bit hacky, but seems to work fine and allows a user to log in with either their name or email address.

Keep in mind there will be issues if you have more than one user with the same email address - email is not a primary/unique fieldtype so even though there are checks to prevent a user from changing their email to one that already exists in the system, it is still possible to create two users with the same email via the admin panel if you are setting them up.

It replaces any instances of "-" with "@" and checks to see if it can find the user with that email address. If there isn't a match, it processes the name with the "-" just in case there is a user in the system with a name that contains a "-", which is allowed.

    public function init() {
        $this->addHookBefore('Session::login', $this, 'beforeSessionLogin');
    }

    public function beforeSessionLogin(HookEvent $event) {
        $name = $event->argumentsByName('name');
        if (strpos($name, '-') !== FALSE) {
            $email = str_replace('-','@',$name);
            $u = wire('users')->get("email=$email");
            $username = $u->id ? $user->name : $name;
            $event->setArgument('name', $username);
        }
    }

I put this together pretty quickly, so can anyone think of any major issues with this?

  • Like 1
Link to comment
Share on other sites

$username = $sanitizer->username($input->post->username); 
$pass = $input->post->pass;
$u = $users->get($username);

if($u->id && $u->tmp_pass && $u->tmp_pass === $pass) {
    ...
}

$u = $session->login($username, $pass);

This is where you try to find the user by the submitted name and then check if you got one by seeing whether $u->id is 0. So what you want to do is, if you didn’t find a user with that name, assume that an e-mail address was submitted instead, and check that:

// Get user by the input name
$username = $sanitizer->username($input->post->username); 
$pass = $input->post->pass; // (password is irrelevant for this, so we don't have to repeat it below)
$u = $users->get($username);

if ($u->id == 0) {
    // no user was found, so let's do the same thing, only
    // this time, treating the input as a mail address
    $usermail = $sanitizer->email($input->post->username); 
    $u = $users->get("email={$usermail}"); // select by matching the mail field
}

// Now do the same checks as before and see if
// the user was found after all
if($u->id && $u->tmp_pass && $u->tmp_pass === $pass) {
    ...
}

// log in with $u->name, the name of the matched
// account, instead of $username. If only the NullUser
// was found, this will be an empty string.
$u = $session->login($u->name, $pass);
  • Like 2
Link to comment
Share on other sites

Hi Adrian, 

this seems to be for an admin login versus a front end login like I have setup. I included the snippet of code a couple posts above. Its the same setup Ryan created. 

Well actually, that hook and function I put together also works perfectly on the front-end, as well as the admin - I assumed you wanted both.

All you need to go is use that as part of a module and then be sure to load the module in your login.php file for it to also work on the front-end.

But if you only need front-end, then Jan Romero's version is simpler.

  • Like 1
Link to comment
Share on other sites

No problem. Also, as a somewhat cleaner alternative to what I posted above, you can grab the POSTed version of the username directly to avoid the need to replace "-" with "@". With this version, it doesn't have to do anything if there is no "@" in the entered username.

    public function init() {
        if (strpos(wire('input')->post->login_name, '@') !== FALSE) {
            $this->addHookBefore('Session::login', $this, 'beforeSessionLogin');
        }
    }

    public function beforeSessionLogin(HookEvent $event) {
        $name = wire('sanitizer')->email(wire('input')->post->login_name);
        $u = wire('users')->get("email=$name");
        if($u->id) $event->setArgument('name', $u->name);
    }
This way you can choose to sanitize it as an email address before it has been sanitized as a pageName which doesn't allow "@".

The disadvantage to this version though is that for the front-end, you need to make sure your custom login form also uses "login_name" as the name of the input field. Not a big deal, but not as versatile I guess.

  • Like 2
Link to comment
Share on other sites

  • 3 weeks later...

Ryan had shown us a nice way to setup user front end login etc. I came across an issue with a client in which I have setup SMTP for a method of delivering notification emails.  However, using Ryan's suggested /reset-pass/ page, the user is not getting notifications. Any light on enabling SMTP for the reset-pass page as well?

Link to comment
Share on other sites

So long as you have one of the SMTP modules installed all you need to do is replace instances of mail() with wireMail() and they will be sent via SMTP. So, look in your reset-pass template code and make that change.

Link to comment
Share on other sites

I am not a formbuilder user, but I know that support for wireMail and hence the ability to use one of the SMTP modules you need the beta version of formbuilder.

How do you know it's not using the SMTP module - is it just that the emails aren't being received? Have you used the test functionality in those modules to make sure they are working?

Have you tried manually calling wireMail() in a script to test the sending and receiving?

Link to comment
Share on other sites

  • 5 months later...

this source code registration member  for processwire  : 

<script type="text/javascript" src="/site/templates/scripts/register.js"></script>
<script type="text/javascript">
$(document).ready(function(){
 
$("#registerform").validate({
debug: false,
rules: {
login_name: {
required: true,
minlength: 6,
remote: "/process/username.php"
},
login_pass: {
required: true,
minlength: 6
},
confirm_pass: {
required: true,
minlength: 6,
equalTo: "#login_pass"
},
email: {
required: true,
email: true,
remote: "/process/emails.php"
}
},
messages: {
login_name: {
required: "Inserisci il tuo username",
minlength: jQuery.format("Inserisci almeno {0} caratteri"),
remote: jQuery.validator.format("Lo username {0} non è disponibile.")
},
login_pass: {
required: "Inserisci la password",
minlength: jQuery.format("Inserisci almeno {0} caratteri")
},
confirm_pass: {
required: "Ripeti la password",
minlength: jQuery.format("Inserisci almeno {0} caratteri"),
equalTo: "Le password non sono uguali"
},
email: {
required: true,
email: "Inserisci una email valida",
remote: jQuery.validator.format("Questa email è già presente nel nostro database.")
},
},
submitHandler: function(form) {
$("#register_submit").attr('value','Attendi...');
$("#register_submit").attr('disabled', 'disabled');
$.post('/process/register.php', $("#registerform").serialize(), function(data) {
if (data=='success'){
var url = "/mioprofilo/";    
$(location).attr('href',url);
}else{
$(".span16").prepend(
    $(data).hide().fadeIn('slow')
   );  
   $(".error").fadeOut(5000);  
$("#register_submit").attr('value','Iscriviti');    
$("#register_submit").removeAttr('disabled');
}
});
}
});
 
 
});
</script>
<div class="row">
<div class="span16">
<fieldset>
<legend></legend>
<form action='/iscrizione/' method='post' id="registerform">
<div class="clearfix"><label for="login_name">Username</label><div class="input"><input type="text" id="login_name" name="login_name" value="" maxlength="50" /></div></div>
<div class="clearfix"><label for="login_pass">Password</label><div class="input"><input type="password" id="login_pass" name="login_pass" value="" /></div></div>
<div class="clearfix"><label for="confirm_pass">Ripeti password</label><div class="input"><input type="password" name="confirm_pass" value="" /></div></div>
<div class="clearfix"><label for="email">Email</label><div class="input"><input type="text" name="email" id="email" value="" maxlength="40" /></div></div>
<div class="actions">
<input type="submit" value="Iscriviti" class="btn primary" name="register_submit" id="register_submit" />
</div>
 
</form>
</fieldset>
</div>
</div>
<?php
require_once('../index.php');
require_once('class.tempmail.php');
$input = wire('input');
$sanitizer = wire('sanitizer');
$roles = wire('roles');
if($input->post->register_submit) {
$username   = $sanitizer->username($input->post->login_name);
$pass     = $input->post->login_pass;
$email   = $sanitizer->email($input->post->email);
$u = new User();
$u->name = $username; 
$u->pass = $pass;
$u->email = $email;
$u->roles->add($roles->get("guest"));
$u->roles->add($roles->get("utente-basic")); // my custom role
$u->save();
       // i add profile picture to every user after registration using 5 different random avatar images.
$pnum = rand(0,5);
$profilephoto = wire('config')->paths->root."site/templates/styles/images/noprofile".$pnum.".jpg";
$u->profilephoto->add($profilephoto);
$u->profilethumb->add($profilephoto);
$u->save();
 
if (wire('session')->login($username, $pass)){
 
$array_content[]=array("username", $username);  
$array_content[]=array("login", $username);  
$array_content[]=array("password", $pass);
 
$admin_id =  "noreply@domain.com";
$user_email  =  $email;
 
sendingemail_phpmailer($array_content, "register.html","class.phpmailer.php","Sitename",$admin_id,$user_email,"Welcome to website");
 
print "success";
}else{
print '<div class="alert-message error">
       <p>Errore durante la registrazione. Riprova.</p>
     </div>';
}
}
?>
Edited by Martijn Geerts
Removed links to a website (spam)
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...