I want to move to Symfony2, because I am totally impressed by its modernity and good programming.
Now I am taking a users table from my old system, with 10,000 users, and I don't want to anger them by making them set a new password....so I want them to be able to login with their old password
Here is pseudo-code of how my users table looks like with 3 major fields concerning login/signup:
id, int(10) unsigned NOT NULL
username varchar(40) NOT NULL
passhash varchar(32) NOT NULL
secret varchar(20) NOT NULL
on signup, the data gets generated this way:
$secret = mksecret ();
$passhash = md5 ($secret . $password_formfield . $secret);
on login, the data gets checked the following way:
if ($row['passhash'] != md5 ($row['secret'] . $password_formfield . $row['secret']))
{
//show login error
}
So how do I handle it best in FOSUserBundle, without having to edit too many files?
You need to create a custom password encoder:
<?php
use Symfony\Component\Security\Core\Encoder\BasePasswordEncoder;
class MyPasswordEncoder extends BasePasswordEncoder
{
public function encodePassword($raw, $salt)
{
return md5($salt.$raw.$salt);
}
public function isPasswordValid($encoded, $raw, $salt)
{
return $this->comparePasswords($encoded, $this->encodePassword($raw, $salt));
}
}
And configure it in security.yml:
services:
my_password_encoder:
class: MyPasswordEncoder
security:
encoders:
FOS\UserBundle\Model\UserInterface: { id: my_password_encoder }
As long as User::getSalt() returns secret and User::getPassword() returns passhash you should be good to go.
It is very easy to do with FOSUserBundle. This is the code for it:
$userManager = $this->get('fos_user.user_manager');
foreach ($items as $item) {
$newItem = $userManager->createUser();
//$newItem->setId($item->getObjId());
// FOSUserBundle required fields
$newItem->setUsername($item->getUsername());
$newItem->setEmail($item->getEmail());
$newItem->setPlainPassword($item->getPassword()); // get original password
$newItem->setEnabled(true);
$userManager->updateUser($newItem, true);
}
Related
I'm trying to migrate users from an old Drupal 6 CMS to Keycloak. I'd like to migrate the users with their old passwords and then assigning an "Update Password" required action to their profile.
However migrating the passwords seems problematic as I can only access them in their hashed form.
The passwords are hashed with an MD5 algorithm using no salt.
I've tried migrating them according to this page:
https://lists.jboss.org/pipermail/keycloak-user/2015-December/004212.html
Here's the JSON I'm sending to the Keycloak REST API:
{
"hashedSaltedValue" : "password-hash",
"algorithm" : "restcomm-md5",
"type" : "password",
}
Here's a list of things I've tried
Included a NULL hash value
Included a 0 hashIteration value
Base64 encoded the hash
Converted the hash to binary and then Base64 encoding it
Has anyone ever had any luck getting this feature working?
The following curl command worked for me to migrate a old hashed password. Replace {hashedSaltedValue} with your hashed password and {salt} with you salt.
token="..."
curl 'http://keycloak-http/auth/admin/realms/testrealm/users/f:60f0ff50-2cc5-492d-8222-04ac0a9964e1:217b93e8-2830-4392-83e3-9feceea94575' \
-X PUT \
-H "Authorization: $token" \
-H "Content-Type: application/json" \
--data '{"credentials": [ { "algorithm": "pbkdf2-sha512", "hashedSaltedValue": "{hashedpassword}", "hashIterations": 30000, "type": "password", "salt":"{salt}"}]}'
The parameters hashedSaltedValue etc. are deprecated and keycloak 10 and newer will log a deprecation warning.
There is a new CredentialRepresentation defined where you put JSON into the strings for attributes secretData and credentialData.
I'm so late, but my answer may be useful for someone. I have the same problem, we don't want to notify our users to reset password. We are creating users by Keycloak Admin REST API java client. Our user's password are hashed by MD5 algorithm. By default KK don't support MD5, that's why firstly we import custom MD5 password hash provider. Below piece of code that help us.
#Test
public void createUser() {
UserDTO user = UserDTO.builder()
.email("dake#mail.ru")
.username("dake#mail.ru")
.emailVerified(true)
.build();
String rawPassword = "barcelona";
String md5Password = "dea56e47f1c62c30b83b70eb281a6c39";
UserRepresentation userRepresentation = convertToUserRepresentation(user);
//setUserRepresentationPassword(userRepresentation, rawPassword, true);
setUserRepresentationPassword(userRepresentation, md5Password, false);
createUser(userRepresentation);
}
public static UserRepresentation convertToUserRepresentation(UserDTO userDTO) {
UserRepresentation userRepresentation = new UserRepresentation();
userRepresentation.setId(userDTO.getId());
userRepresentation.setEnabled(true);
userRepresentation.setUsername(userDTO.getUsername());
userRepresentation.setFirstName(userDTO.getFirstName());
userRepresentation.setLastName(userDTO.getLastName());
userRepresentation.setEmail(userDTO.getEmail());
userRepresentation.setEmailVerified(userDTO.isEmailVerified());
userRepresentation.singleAttribute("cityId", userDTO.getCityId() != null ? "" + userDTO.getCityId() : null);
userRepresentation.singleAttribute("phone", userDTO.getPhone());
userRepresentation.singleAttribute("phoneVerified", "" + userDTO.isPhoneVerified());
userRepresentation.singleAttribute("notificationsEnabled", "" + userDTO.isNotificationsEnabled());
return userRepresentation;
}
/**
* #return User uuid
*/
public String createUser(UserRepresentation userRepresentation) {
if (CollectionUtils.isEmpty(userRepresentation.getGroups())) {
userRepresentation.setGroups(Arrays.asList(GROUP_USERS));
}
RealmResource realm = keycloak.realm(realmName);
Response response = realm.users().create(userRepresentation);
if (response.getStatus() < 200 || response.getStatus() > 299) {
String error = "User create error: " + response.readEntity(String.class);
log.error(error);
throw new RuntimeException(error);
}
// Extract the uuid of the user we just created.
String location = response.getMetadata().get("Location").get(0).toString();
String uuid = location.substring(location.lastIndexOf("/") + 1);
log.info("User created: " + uuid);
return uuid;
}
/**
* Set password for user
*
* #param userRepresentation user
* #param password raw(plaintext) password or hashed password(this way is deprecated)
* #param isRawPassword password is plaintext
*/
#SneakyThrows
public static void setUserRepresentationPassword(UserRepresentation userRepresentation, String password, boolean isRawPassword) {
CredentialRepresentation credential = new CredentialRepresentation();
credential.setType(CredentialRepresentation.PASSWORD);
credential.setTemporary(false);
if (isRawPassword) {
credential.setValue(password);
} else {
Field algorithm = credential.getClass().getDeclaredField("algorithm");
algorithm.setAccessible(true);
algorithm.set(credential, "MD5");
Field hashIterations = credential.getClass().getDeclaredField("hashIterations");
hashIterations.setAccessible(true);
hashIterations.set(credential, 0);
Field hashedSaltedValue = credential.getClass().getDeclaredField("hashedSaltedValue");
hashedSaltedValue.setAccessible(true);
hashedSaltedValue.set(credential, password);
}
userRepresentation.setCredentials(Arrays.asList(credential));
}
After that everything is good. I noticed, after I logged in my MD5 password are automatically converted to pbkdf2-sha256.
Keycloak reset-password api is, what you're trying to use?
Using "reset-password" api, I believe it only accepts plain text password, which means, you can't reset-password with already hashed password value.
If you use create user api, then you can add hashed value as password.
I am using Aerobase with Keycloak and try to update password using reset-password api, it's not working with hashed password, it only works with plain text password and then store hashed password instead.
If there's anyone who's successfully reset-password with hashed password, please leave comment here!
I am working on prestashop web services for my android app, i searched a lot in google, but i did not find proper document/proper explanation. Can any one please guide me how to do user login/authentication using prestashop web services?.
You can make a call to the customer endpoint filtering by the customer's email. The result would have a "passwd" field, which is a hashed password which could either be md5 or bcrypt since Prestashop supports both. You can then hash the customer's password input and compare with the "passwd" field in the response. If the length of the "passwd" field is 32 (md5), you'll need parameter _COOKIE_KEY_ set in /app/config/parameters.php as a salt to generate your hash;
Make the call like this https://yourprestashopurl.com/api/customers?filter[email]=email#email.com&display=full
For md5 (if passwd is 32 characters long):
$hash = md5(_COOKIE_KEY_ . $input_password);
You can then compare $hash with passwd
For bcrypt (if passwd is 60 characters long):
Option 1:
$verify = password_verify($input_password, passwd);
You can accept $input_password when this returns true, otherwise $input_password is invalid
Option 2:
$hash = password_hash($input_password, PASSWORD_BCRYPT);
You can then compare $hash with passwd
Note: password_hash and password_verify are both built-in php functions since PHP 5.5.0
Use the PrestaShop webservices and filter with email and password like below:
http://localhost/api/customers/?filter[email]=test#prestashop.com&filter[password]=19910794b7c0b413e80f58298a8d8300
For those who are still searching for this answer:
<?php
if (isset($_GET["email"]) && isset($_GET["password"]))
{
$email = $_GET["email"];
$password = $_GET["password"];
$COOKIE_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
$jsonurl = "https://XXXXXXXXXXXXXXXXXXXX#example.com/api/customers?filter[email]=".$email."&display=[passwd]&output_format=JSON";
$json = file_get_contents($jsonurl);
$json_a = json_decode($json, true);
$loopone = $json_a['customers'];
$looptwo = $loopone[0];
$loopthree = $looptwo['passwd'];
$ZCpassword = md5($COOKIE_KEY . $password);
if (strcmp($loopthree, $ZCpassword) == 0) {
echo "sucess";
} else {
echo "fail";
}
}
else
{
echo "Error";
}
?>
Following the Lumen doc (http://lumen.laravel.com/docs/authentication), I'm successfully authenticating my website users with the following code :
if( Auth::attempt($request->only('email', 'password') ) ) {
// success
}
But I can't get the remember me functionnality working properly. This is my code :
$remember = (bool) $request->input('remember');
if( Auth::attempt($request->only('email', 'password'),$remember ) ) {
// success
}
And my database "users" table has a column named remember_token, which is a varchar(100) NULL (mysql).
The users are still being authenticated successfully but the remember_token is not filled (always null) and my users are not remembered.
How can I make it work?
I have to modify the password of an account on a vtiger crm. The problem is that I don't know the location of the database.
Anyone know the path of the database containing the credential of the users?
If your username starts with 'ad' like 'admin'. use the following mysql query
UPDATE vtiger_users SET user_password = '$1$ad000000$mnnPAFfqzJOuoYY7aB.mR0' WHERE user_name='admin';
This query will reset the password for user with admin username. The password will be set to password.
Vtiger use encrypt_password function in Users.php on line 264 to encrypt user password.
modules/Users/Users.php
It use crypt_type and username for encrypt new passwords. so Mysql query only work if your username starts with ad for example 'admin' , 'adam' and etc.
function encrypt_password($user_password, $crypt_type='') {
// encrypt the password.
$salt = substr($this->column_fields["user_name"], 0, 2);
// Fix for: http://trac.vtiger.com/cgi-bin/trac.cgi/ticket/4923
if($crypt_type == '') {
// Try to get the crypt_type which is in database for the user
$crypt_type = $this->get_user_crypt_type();
}
// For more details on salt format look at: http://in.php.net/crypt
if($crypt_type == 'MD5') {
$salt = '$1$' . $salt . '$';
} elseif($crypt_type == 'BLOWFISH') {
$salt = '$2$' . $salt . '$';
} elseif($crypt_type == 'PHP5.3MD5') {
//only change salt for php 5.3 or higher version for backward
//compactibility.
//crypt API is lot stricter in taking the value for salt.
$salt = '$1$' . str_pad($salt, 9, '0');
}
$encrypted_password = crypt($user_password, $salt);
return $encrypted_password;
}
You can use the following tools on Github. it can change all users password without login into crm and phpmyadmin and update vtiger user privileges file.
https://github.com/spadana2004/Vtiger-CRM-Reset-Password-Tools
Go to My preferences(right top of the browser). There you can change the password of the user.
In database you can't change bcoz there it will be converted to MD5. Then also for your kind information in database check the table vtiger_users for user detail.
update vtiger_users set user_password = 'adpexzg3FUZAk', crypt_type = '' where id = '1';
Login: admin
Password:admin
To make this really easy for admins I created a simple gist that will generate the sql query you need to run to reset a password with any username. Just put in the username and temp password, then run the script and use the SQL it provides. After that just login with that username and password. It is tested with and working on VTiger 7.2.
https://gist.github.com/mav2287/59d5587c7efabdbb105b739c4bc27cb5
<?php
// Put in your username as found in the "vtiger_users" table under the "username" column
$user_name = "";
// Set your TEMPORARY password. You NEED to reset your password after using this to reset it.
$user_password = "password";
// return the approiate stamtent
echo "Run the following SQL query to reset your password: \n";
echo "\"UPDATE vtiger_users SET user_password='".crypt($user_password, substr($user_name, 0, 2))."',crypt_type=''WHERE user_name='".$user_name."'\"";
I have a good start on a technique similar to this in Express 3
http://notjustburritos.tumblr.com/post/22682186189/socket-io-and-express-3
the idea being to let me grab the session object from within a socket.io connection callback, storing sessions via connect-redis in this case.
So, in app.configure we have
var db = require('connect-redis')(express)
....
app.configure(function(){
....
app.use(express.cookieParser(SITE_SECRET));
app.use(express.session({ store: new db }));
And in the app code there is
var redis_client = require('redis').createClient()
io.set('authorization', function(data, accept) {
if (!data.headers.cookie) {
return accept('Sesssion cookie required.', false)
}
data.cookie = require('cookie').parse(data.headers.cookie);
/* verify the signature of the session cookie. */
//data.cookie = require('cookie').parse(data.cookie, SITE_SECRET);
data.sessionID = data.cookie['connect.sid']
redis_client.get(data.sessionID, function(err, session) {
if (err) {
return accept('Error in session store.', false)
} else if (!session) {
return accept('Session not found.', false)
}
// success! we're authenticated with a known session.
data.session = session
return accept(null, true)
})
})
The sessions are being saved to redis, the keys look like this:
redis 127.0.0.1:6379> KEYS *
1) "sess:lpeNPnHmQ2f442rE87Y6X28C"
2) "sess:qsWvzubzparNHNoPyNN/CdVw"
and the values are unencrypted JSON. So far so good.
The cookie header, however, contains something like
{ 'connect.sid': 's:lpeNPnHmQ2f442rE87Y6X28C.obCv2x2NT05ieqkmzHnE0VZKDNnqGkcxeQAEVoeoeiU' }
So now the SessionStore and the connect.sid don't match, because the signature part (after the .) is stripped from the SessionStore version.
Question is, is is safe to just truncate out the SID part of the cookie (lpeNPnHmQ2f442rE87Y6X28C) and match based on that, or should the signature part be verified? If so, how?
rather than hacking around with private methods and internals of Connect, that were NOT meant to be used this way, this NPM does a good job of wrapping socket.on in a method that pulls in the session, and parses and verifies
https://github.com/functioncallback/session.socket.io
Just use cookie-signature module, as recommended by the comment lines in Connect's utils.js.
var cookie = require('cookie-signature');
//assuming you already put the session id from the client in a var called "sid"
var sid = cookies['connect.sid'];
sid = cookie.unsign(sid.slice(2),yourSecret);
if (sid == "false") {
//cookie validation failure
//uh oh. Handle this error
} else {
sid = "sess:" + sid;
//proceed to retrieve from store
}