I have a Laravel 4 app in which I have set up one user. In my login route I'm calling Auth::attempt with the email and password but it always comes back as false. I definitely have the password correct and the correct hash in the database as Hash::check returns true.
I think it may be due to using email as the login field instead of username, but I can't see any setting for that. This question implied you could add an option to config/auth.php but it didn't work. This question says to use username as the array key, but then I get SQL error because it tries to select on a username field in the database.
Do I need to add something to the User model to specify the username field? Here is my login route:
Route::post('login', function() {
// data from login form
$credentials = array(
'email' => Input::get('email'),
'password' => Input::get('password')
);
$auth = Hash::check(Input::get('password'), Hash::make('mypass'));
var_dump($auth); // this is TRUE
// login was good
$auth = Auth::attempt($credentials);
var_dump($auth); // this is FALSE
});
I found the problem. As suggested by Jason in the comment above, I had modified the models/User.php file and removed some functions that I didn't realise were necessary.
The getAuthIdentifier() and getAuthPassword() methods must be included in the User model for authentication!
In app/config/app.php make sure you have the 'key' set. This made me pull my hair out. Everything will apear to work, password seems hashed in the DB, but it will always return false until you set this key and re-hash your password into the DB.
"php artisan key:generate"
Had the same problem and made me sweat for hours. Definitively check your User.php model and make sure you have not overwritten the default one. Thanks Jason!
Related
I'm trying to add an Item, where one of the fields is of type contact (user), to Podio.
I do not have the contact profile_id, only the name, so I need to search the contact to get the profile_id before adding.
The problem is that the /contact/ resources are inaccessible since I'm using app authentication.
The error is: "Authentication as app is not allowed for this method"
What is the recommended way to do this?
Thanks.
As I can see, the tricky part here is that you have just a name of the user. So you need to search this name first.
To be able to search you should be authenticated not as an app, but as a user with appropriate rights. I believe this is because search functions a rate-limited per user. You may authenticate on client side, server side or just by entering user's email and password (see documentation here).
Then, when authenticated, just use search functions with the parameter "ref_type": "profile" to look for the user name within space, organisation or globally. Example for PHP-client:
$attributes = array(
"query" => "John Doe",
"ref_type" => "profile"
);
$results = PodioSearchResult::space( $space_id, $attributes ); // search in space
$results = PodioSearchResult::org( $org_id, $attributes ); // search in organisation
$results = PodioSearchResult::search( $attributes ); // search globally
Functions above will return an array of the most relative results found. There you can get a user id and other user info.
Note that technically several different users may have the same name, so there might be more that one result found. It will be up to you to choose one of them somehow.
It is possible to check user by credentials without login for:
exits user
password is correct
is not banned
is activated
...
As I can see, I must write a lot of code for this. Maybe exists any easiest way?
Check the user is valid without logging in with Auth (Native):
$credentials = array('email' => 'xx', 'password' => 'xx', 'deleted_at' => null);
if(Auth::attempt($credentials, false, false)) {
// Exists and password is correct and not banned
}
This requires you to keep the deleted_at field in your table to soft deleting technique (By disabling or enabling a user temporarily).
If you are using a different field to make a user inactive/active then pass that field instead of deleted_at, for example, you may use a field active with value 1 to indicate an active user and 0 for an inactive user, in this case pass active => 1 to check the active field has value 1 as the third item in the credentials array.
I have added a column in my wikidatabase in the user table called approved_account.
The standard value on that column is 0 (zero).
I would like to add an exception when a user tries to log in to the wiki, such that
if approved_account = 0 then the login attempt is denied.
Does anyone know how and where I should place that if statement?
Edit: I've come this far.
I am using the AbortLogin hook, since I need to verify if my statement is true every time a user tries to log in.
However, my code won't let anyone in. It blocks all login attempts, even if I have the correct value in the approved_account field.
Can anyone help me fix this?
<?php
/**
* Prevent a user from accessing this file directly and provide a helpful
* message explaining how to install this extension.
*/
if ( !defined( 'MEDIAWIKI' ) ) {
echo <<<EOT
To install the Test extension, put the following line in your LocalSettings.php file:
require_once( "$IP/extensions/approvedaccount.php" );
EOT;
exit( 1 );
}
// Extension credits that will show up on Special:Version
$wgExtensionCredits['parserhook'][] = array(
'name' => 'Approved Account extension',
'description' => 'Prevent login',
'author' => 'Me',
'url' => 'http://www.mediawiki.org/wiki/Extension:approvedaccount'
);
$wgHooks['AbortLogin'][] = 'approvedaccount::onAbortLogin';
class approvedaccount
{
public static function onAbortLogin( $user, $password, &$retval ) {
global $wgOut, $wgUser;
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select(
'user', // $table
array( 'user_name', 'approved_account' ), // $vars (columns of the table)
'user_name = "'.$wgUser.'"', // $conds
__METHOD__, // $fname = 'Database::select',
array( 'ORDER BY' => 'user_name ASC' ) // $options = array()
);
$output = '';
foreach( $res as $row ) {
$output .= 'Användarnamn: ' . $row->user_name . ' , Approved Account: ' . $row->approved_account . ".";
}
if ($row->approved_account = "1"){
//$this->loadDefaults();
// return false;
header("Location: http://hbg-whirlpool.emea.stream.corp/index.php?title=Special:UserLogout&returnto=Main+Page");
exit(); // you need to exit after a Location header is sent
}
}
}
You could do this with a simple AuthPlugin, overriding the strictUserAuth() method to return true for users that match the condition.
However, I suspect you're approaching this problem the wrong way. Why not just define a new user group, say, approved, and then add the corresponding record to the user_groups table for approved users? You won't be able to prevent unapproved users from logging in, but you can prevent them from making edits by only granting the edit permission to the approved group, like this:
$wgGroupPermissions['*']['edit'] = false;
$wgGroupPermissions['user']['edit'] = false;
$wgGroupPermissions['approved']['edit'] = true;
(If you wanted, you could even revoke the read permission from unapproved users too, but please read the warnings about restricting read access in MediaWiki first.)
Edit: I see a couple of problems with your AbortLogin hook.
Doing a 301 redirect and an exit() in the middle of the hook is probably not a very good idea. Sure, it probably will abort the login, but that's not really how the hook is meant to be used. Rather, you should just have the hook function return false to indicate that the login should be aborted or true to proceed with the normal login checks.
In any case, you're doing the exit() when the approved_account column is 1, which is presumably exactly when you don't want to abort the login.
...or, rather, you're doing the exit() always, because you used the assignment operator = instead of the comparison operator == in the condition, causing it to be always true. (Don't worry, that's a common bug in PHP and other C-like languages. One way to avoid is to get in the habit of using "Yoda conditionals" like 1 == $row->approved_account, which will produce an error if you leave out one =, since you can't assign to 1.)
Also, concatenating a User object with a string probably won't produce anything meaningful; and, even if it did, there would be an SQL injection vulnerability there. And besides, the hook parameters already include a User object, so you should use that instead of the global $wgUser (which might be stale during login anyway).
I admit that some of this stuff is really poorly documented. Besides the AbortLogin docs, I'd suggest looking at the general MediaWiki hook documentation, as well as the actual way the hook is called from SpecialUserlogin.php. For the database access, I'd also point you to the database wrapper function docs; unfortunately, the method documentation pages are giving 404 errors right now, so you'd again need to look directly in the source for the documentation.
Anyway, I'd rewrite your hook like this:
public static function onAbortLogin( $user, $password, &$retval, &$msg ) {
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow(
'user',
'approved_account',
array( 'user_id' => $user->getID() ),
__METHOD__
);
if ( !$row || !$row->approved_account ) {
$retval = LoginForm::ABORTED; // actually the default, but let's be sure
$msg = 'login-abort-not-approved'; // optional: custom error message
return false;
}
else {
// account is approved, return true to proceed with other login checks
return true;
}
}
If you want the custom message, you'll also need to create the page MediaWiki:login-abort-not-approved on your wiki. (If you wanted to turn this into a proper MediaWiki extension, you could provide a default message in an i18n file, but that's probably overkill here.)
Edit 2: Yes, you can add as many hooks as you want in an extension. (In fact, you don't even need an extension, it's perfectly fine to define simple site-specific hooks directly in LocalSettings.php if you want.) I think something like this could work for an AddNewAccount hook to log the user out, although I must note that I haven't actually tested this:
public static function onAddNewAccount( $user, $byEmail ) {
global $wgUser;
// try to log out the new user only if they're actually logged in
if ( $user->getName() == $wgUser->getName() ) $user->logout();
return true;
}
The if clause is there because the AddNewAccount is also called when a user creates a new account while logged in to a pre-existing account, in which case logging them out from their original account would be an unwelcome surprise. (Technically, just if ( $user == $wgUser ) ought to suffice, but explicitly comparing the usernames rather than the object references seems safer.)
Note that logging the new user out at that point kind of yanks the carpet out from under the new user creation code, so some unusual things may happen. For example, I suspect that the user creation log may actually end up saying something like "NewUserName created the new user account NewUserName", and the "Account successfully created" page may temporarily show the user as logged in, even though they're actually not.
It would be much cleaner to somehow avoid the auto-login behavior in the first place, but I don't see any obvious way to do that without patching SpecialUserlogin.php: the only check that determines whether the new user is automatically logged in is if ( $this->getUser()->isAnon() ), which only checks whether a user is already logged in. Even faking that somehow (which would be an ugly kluge in itself) doesn't really seem practical, as far as I can tell.
If you don't mind patching the MediaWiki core, though, just replacing that condition with if ( false ) (or if ( false && $this->getUser()->isAnon() ), if you want to keep it self-documenting) should do the trick. Note that you could still keep the AddNewAccount hook as a backup, in case you forget to reapply the patch after upgrading or something.
I don't use RBAC to validate users. I wouldn't mind using it if it's possible, but I don't think it is. Reason being, I use a REST API to validate users. I have this in my authenticate() function:
$API = new API();
$user = $API->getAccountDetailsByEmail($this->username);
if($user->password !== md5($this->password) ) {
// Validated
}
I want the user to also be assigned a role at this step. Which is why I tried the following below the above:
$this->setState('roles', 'admin');
But this doesn't work at all. I still get:
Error 403: You are not authorized to perform this action.
When I go to the page I am trying to make admin accessible. How do I programmatically set a user as an admin?
Am I missing something, or is there an easy way to assign a role to a user that was authenticated?
The CAccessControlFilter relies on the CWebUser::checkAccess() function. This function is called with the name of the role as a parameter. If you do not want RBAC then the easiest you could do is write your own CWebUser derived class and implement your own checkAccess.
You can activate this class in your config file by adding the "user" component:
'components'=> array
(
'user' => array
(
'class' => 'MyWebUser',
),
),
You could for example set a list of roles in the users' session and have the function check if the user has that role. Although I would advise against using the session to store roles (the database is beter) using setState is definitely a bad idea. IIRC this sets a cookie on the user side and a bit of an inventive user could figure out how to abuse this.
If your action rules are
array('allow',
'actions'=>array(
'myAction',
),
'users'=>array('#'),
'roles'=>array('admin'),
),
Then change them to:
array('allow',
'actions'=>array(
'myAction',
),
'users'=>array('#'),
'expression'=>'$user->getState("roles")=="admin"',
),
The roles parameter for action rules is for use ONLY with RBAC. So you need to do your validation differently if you aren't using RBAC.
If that isn't your issue, then please provide more details about what you are trying and what your access rules look like.
My Users table (the one that I created) has the following columns:
UserId,UserName,FirstName,LastName,DOB
After I ran this command
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "Users", "UserId", "UserName", autoCreateTables: true);
it created the required simple membership tables for me.
How would I go about "UnConfirming" an user or setting the "IsConfirmed" flag to false in the webpages_Membership using the new SimpleMembership API?
(Earlier, before going to simplemembership using the "Membership" class I could update an user using the api call : Membership.UpdateUser( user );)
I can't answer your question directly since I couldn't figure out a way to 'unconfirm' an account either. What I ended up doing, however, may help whoever finds this question.
I basically use Roles as a gatekeeper. Whenever I create a new account I add that user to a "User" role:
Roles.AddUserToRole(newUser.Username, "User");
I use the Authorize attribute to restrict access to my controllers (and use [AllowAnonymous] for actions that I want to be public -- like RegisterUser, for example). Then, inside each action I add a method to restrict access to only users that are in the "User" role.
if (!Roles.IsUserInRole(role))
{
throw new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.Unauthorized));
}
NOTE: I'm using Web API, but if you're using MVC you should have a much easier time. Instead of manually checking if a user is in a role in each action you can just use the authorize attribute:
[Authorize(Roles = "User")]
When I want to "UnConfirm" a user I just remove them from the "User" role.
Roles.RemoveUserFromRole(user.Username, "User");
This way if a user comes crawling back I can just reactivate their account by adding them back as a User.
What I ended up doing was updating that table directly via a SQL query. Not sure if thats the recommended way of doing it, but that seemed to work for me.
(Thanks for your suggestion too).
Look at this blog post on adding email confirmation to SimpleMembership registration process, which covers how the confirmation process works. The cliff notes are that when you create a new user you set the flag that you want to use confirmation like this.
string confirmationToken =
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { Email = model.Email }, true);
When you do this the CreateUserAndAccount method returns a unique token that you can put in an email with a link so the user can confirm that they gave you a valid email address. When they click on the link it passes the token in the URL and the controller action can then confirm the token like this.
[AllowAnonymous]
public ActionResult RegisterConfirmation(string Id)
{
if (WebSecurity.ConfirmAccount(Id))
{
return RedirectToAction("ConfirmationSuccess");
}
return RedirectToAction("ConfirmationFailure");
}
The ConfirmAccount method checks if there is an uncomfirmed token that matches in the database and if there is it sets the isConfirmed flag to true. The user will not be able to logon until this is set to true.
set requireConfirmationToken to be true: (The 4th value shown below)
WebSecurity.CreateUserAndAccount(viewModel.UserName, viewModel.Password, null, true);
Source
http://www.w3schools.com/aspnet/met_websecurity_createuserandaccount.asp