Change password function not working - hash wrong - cakephp

Ok, I've been hitting my head against this wall all evening.
Can someone explain to my why this returns false (user model):
public function changePassword($user_id, $currentPassword, $newPassword, $repeatPassword){
//Check repeat
/*
if($newPassword != $repeatPassword)
return false;
*/
//Check old password
$this->id = $user_id;
$current = $this->field('password');
$passwordHasher = new BlowfishPasswordHasher();
$hash = $passwordHasher->hash($currentPassword);
if($current != $hash)
return false;
//set password to data
//save
return true;
}
public function beforeSave($options = array()) {
if(isset($this->data[$this->alias]['password'])) {
$passwordHasher = new BlowfishPasswordHasher();
$this->data[$this->alias]['password'] = $passwordHasher->hash($this->data[$this->alias]['password']);
}
return true;
}
I can see from debugging $current and $hash that the generated hash is not the same as the one pulled from the database. Question is why.
Login works fine by the way. CakePHP version is 2.6.5
EDIT:
Problem solved. Complete solution here:
public function changePassword($user_id, $currentPassword, $newPassword, $repeatPassword){
//Check repeat
if($newPassword != $repeatPassword)
return false;
//Check old password
$this->id = $user_id;
$current = $this->field('password');
$passwordHasher = new BlowfishPasswordHasher();
if(!$passwordHasher->check($currentPassword, $current))
return false;
//set password to data
$this->data['password'] = $newPassword;
//save
if(!$this->save($this->data))
return false;
return true;
}
public function beforeSave($options = array()) {
if(isset($this->data[$this->alias]['password'])) {
$passwordHasher = new BlowfishPasswordHasher();
$this->data[$this->alias]['password'] = $passwordHasher->hash($this->data[$this->alias]['password']);
}
return true;
}

$current and $hash that the generated hash is not the same
That's how blowfish works. It generates a new hash each time.
Instead of hashing the current password and doing string comparison with existing hash from datbase use BlowfishPasswordHasher::check() to check if current password matches hash from database.

Related

Identity Server 4. Token Endpoint, Password Grant. How do I check acr_values?

I suspect this is very simple but I can't find exactly what I need to do.
I am trying to get a password Grant enabled, token endpoint, working with name/password/SomethingExtra
It all works with just name and password. I can see my "Data:SomethingExtra" in acr_values on the server but only in the GetProfileDataAsync override.
So, I can pick up the acr_values in the token generation (GetProfileDataAsync) but I want to "validate" the user with this extra data. How do I test my acr_values at validation?
I suspect there is a method I can override to pick up the "login request" with the extra acr_values so I can decide to return a token or Access Denied much the same was I do with an interactive login using Quickstart web pages.
But what do I override to allow me to authenticate against 3 values?
If you are using password grant then you will have implemented IResourceOwnerPasswordValidator to validate your passwords. You can get the acr values in this class as follows:
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) {
string acrValues = context.Request.Raw.Get("acr_values");
IEnumerable<string> values = acrValues.Trim().Split(new[] { ' ' });
string extraDataAcr = values.FirstOrDefault(x => x.StartsWith("ExtraData:"));
string extraDataValue extraDataAcr?.Substring("ExtraData:".Length);
After a bit of digging...might save someone some time
public class ACustomTokenRequestValidator : ICustomTokenRequestValidator
{
private readonly UserManager<ApplicationUser> _userManager;
public ACustomTokenRequestValidator(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task ValidateAsync(CustomTokenRequestValidationContext context)
{
if (context?.Result?.ValidatedRequest?.GrantType != null &&
context.Result.ValidatedRequest.GrantType.ToUpper() == "PASSWORD") // Only interested in password Grant
{
var acr_values = new Dictionary<string, string>();
string username = string.Empty;
string error = string.Empty;
string Tester = string.Empty;
bool ok = true;
if (context.Result.ValidatedRequest.Raw != null)
{
var reqParamsDict = context.Result.ValidatedRequest.Raw.ToDictionary();
if (reqParamsDict.ContainsKey("acr_values"))
{
var raw = reqParamsDict["acr_values"].Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
acr_values = raw.Select(item => item.Split(':', 2, StringSplitOptions.RemoveEmptyEntries)).ToDictionary(s => s[0], s => s[1]);
}
if (reqParamsDict.ContainsKey("username")) // Should always be there, name/password check would have failed already
{
username = reqParamsDict["username"];
}
else
{
ok = false;
error = "username missing from request";
}
if (ok && acr_values.ContainsKey("ExtraField")) // Could be missing
{
Tester = acr_values["ExtraField"];
}
else
{
ok = false;
error = "ExtraField missing from request";
}
if (ok)
{
if (context.Result.ValidatedRequest.Scopes.Contains("API_Name"))
{
var user = await _userManager.FindByNameAsync(username);
if (user != null)
{
if ( user.ExtraField != Tester )
{
ok = false;
error = "Failed extra test";
}
}
else
{
ok = false;
error = "User not found";
}
}
}
}
if (!ok)
{
context.Result.IsError = true;
context.Result.Error = error;
}
}
}
}
For completeness this is my Postman setup to get it working
[https://i.stack.imgur.com/BtihJ.png][1]

Laravel - Insert & delete rows upon updating

I have one-to-many Model relationship. While creating a new record, I can save as many rows as I want. But I want to add new row or delete saved row after Update operation. How would I do it?
Item Model
public function models() {
return $this->hasMany(Model::class, 'item_id');
}
Model Model
public function model()
{
return $this->belongsTo(Item::class, 'item_id');
}
Controller
Store
public function store(Request $request, Item $item)
{
$item = new Item;
$item->title = request('title');
if($item->save())
{
for ($i=0; $i < count(request('model_name')); ++$i)
{
$model = new Model;
$model->name = request('name')[$i];
$model->price = request('price')[$i];
}
return redirect()->back();
}
Update
public function update(Request $request, $id){
$item = Item::findOrFail($id);
$data = $request->all()
$models = Model::with(['model'])->where('item_id', $item->id)->get();
$i=0;
foreach($models as $new_model)
{
if (isset($model_name[$i])){
$new_model->sku = request('name')[$i];
$new_model->price= request('price')[$i];
$++i;
$shnp->models()->save($new_model);
}
}
$item->update($data);
}
Edit Form
Database Table

Can't get Novell.Directory.Ldap.NETStandard library to query

I need to let the user query an Active Directory for names in .Net Core.
So I am building an Active Directory Search Web API Service.
I am able to connect with the bind statement.
But I am not able to get any results back with my query although there is no error.
Another programmer sent me some code he uses in other applications. But it uses the DirectoryEntry object which is not available in .Net Core.
So I am trying to use the Novell.Directory.Ldap.NetStandard library.
Here is the code the other developer sent me:
public static List<UserProfileModel> GetADUsers(string alias)
{
List<UserProfileModel> users = new List<UserProfileModel>();
if (alias == null || alias.Trim().Equals(""))
{
return users;
}
try
{
// Ad path LDAP://ourOrg.gov/CN=Users,DC=ourOrg,DC=gov
DirectoryEntry de2 = new DirectoryEntry(ConfigurationManager.AppSettings["AD_Path"], ConfigurationManager.AppSettings["AD_User"], ConfigurationManager.AppSettings["AD_Password"]);
de2.Path = ConfigurationManager.AppSettings["AD_Path"];
de2.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher deSearch = new DirectorySearcher();
deSearch.SearchRoot = de2;
deSearch.Filter = "(samaccountname=*" + alias + "*)";
LOGGER.Debug(String.Format("Active Directory Search Filter {0}", deSearch.Filter));
SearchResultCollection results = deSearch.FindAll();
String raw = "";
LOGGER.Debug(String.Format("Active Directory Search Result Counts {0}", results.Count));
if (results.Count > 0)
{
foreach (SearchResult item in results)
{
UserProfileModel userProfileModel = new UserProfileModel();
userProfileModel.Name = GetADProperty("name", item);
userProfileModel.email = GetADProperty("mail", item);
userProfileModel.identity = GetADProperty("userPrincipalName", item);
userProfileModel.first_name = GetADProperty("givenName", item);
userProfileModel.last_name = GetADProperty("sn", item);
users.Add(userProfileModel);
raw = String.Format("{0}/n{1}", raw, userProfileModel.ToString());
}
LOGGER.Debug(String.Format("Active Directory Search Resuts ToString: {0}", raw));
}
}
catch (Exception e)
{
LOGGER.Error("Unable to Query Active Directory", e);
}
return users;
}
I need to translate this into Novell's LDAP library.
Here is my attempt:
[HttpGet]
public async Task<List<UserProfileModel>> GetByName(string alias)
{
int ldapPort = LdapConnection.DEFAULT_PORT;
string ldapHost = "ourOrg.gov";
string loginDn = #"ourOrg\myName";
string password = "myPass";
List<UserProfileModel> users = new List<UserProfileModel>();
if (alias == null || alias.Trim().Equals(""))
{
return users;
}
try
{
using (var con = new LdapConnection())
{
con.Connect(ldapHost, ldapPort);
con.Bind(loginDn, password);
LdapSearchResults results = con.Search(
"cn=users,dc=ourOrg,dc=gov",
LdapConnection.SCOPE_ONE,
"samaccountname=*",
null,
false);
// NO RESULTS:(
}
return users;
}
catch(Exception ex)
{
throw ex;
}
}
I don't get an error.
But there are 0 results.
I originally had this part:
"samaccountname=*",
like:
"samaccountname={alias}",
but I'm just trying to get back results at this point.
I got this working:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Hrsa.Core.Web.App.Models.ViewModels;
using Novell.Directory.Ldap;
// For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
namespace Hrsa.Core.Web.App.Controllers.Api
{
[Route("api/[controller]")]
public class ActiveDirectoryController : Controller
{
private readonly AppSettings _appSettings;
public ActiveDirectoryController(IOptions<AppSettings> appSettings)
{
_appSettings = appSettings.Value;
}
[HttpGet]
public async Task<List<UserProfileModel>> GetByName(string alias)
{
int ldapPort = LdapConnection.DEFAULT_PORT;
string ldapHost = _appSettings.HrsaLdapHost; // ourOrgName.gov
string loginDn = _appSettings.AdUser;
string password = _appSettings.AdPassword;
string searchBase = _appSettings.HrsaAdSearchBase;
string searchFilter = $"(samaccountname=*{alias}*)";
string[] attributes = new string[] { "cn", "userPrincipalName", "st", "givenname", "samaccountname",
"description", "telephonenumber", "department", "displayname", "name", "mail", "givenName", "sn" };
List<UserProfileModel> users = new List<UserProfileModel>();
if (alias == null || alias.Trim().Equals(""))
{
return users;
}
try
{
using (var con = new LdapConnection())
{
con.Connect(ldapHost, ldapPort);
con.Bind(loginDn, password);
LdapSearchQueue queue = con.Search(
searchBase,
LdapConnection.SCOPE_SUB,
searchFilter,
attributes,
false,
(LdapSearchQueue)null,
(LdapSearchConstraints)null);
LdapMessage message;
while ((message = queue.getResponse()) != null)
{
if (message is LdapSearchResult)
{
LdapEntry entry = ((LdapSearchResult)message).Entry;
LdapAttributeSet attributeSet = entry.getAttributeSet();
users.Add(new UserProfileModel
{
Cn = attributeSet.getAttribute("cn")?.StringValue,
UserPrincipalName = attributeSet.getAttribute("userPrincipalName")?.StringValue,
St = attributeSet.getAttribute("st")?.StringValue,
Givenname = attributeSet.getAttribute("givenname")?.StringValue,
Samaccountname = attributeSet.getAttribute("samaccountname")?.StringValue,
Description = attributeSet.getAttribute("description")?.StringValue,
Telephonenumber = attributeSet.getAttribute("telephonenumber")?.StringValue,
Department = attributeSet.getAttribute("department")?.StringValue,
Displayname = attributeSet.getAttribute("displayname")?.StringValue,
Name = attributeSet.getAttribute("name")?.StringValue,
Mail = attributeSet.getAttribute("mail")?.StringValue,
GivenName = attributeSet.getAttribute("givenName")?.StringValue,
Sn = attributeSet.getAttribute("sn")?.StringValue
});
}
}
}
return users;
}
catch(Exception ex)
{
throw ex;
}
}
}
}

How to use Roles in ASP.NET MVC 5 web app with LocalDB?

I would like to manage users and roles with the default ASP.NET MVC 5 membership in a LocalDB database file in the app_data folder. This is automatically created if it does not exist.
I wrote a role editor, but when I attempt to apply roles to the Web API as follows:
[Authorize(Roles= "SystemAdmin")]
, the role manager attempts to call a stored procedure inside the database.
Invalid object name 'dbo.aspnet_SchemaVersions'.
This stored procedure would normally be created in a full SQL Server membership database created by the aspnet_regsql utility, however the aspnet_regsql utility only operates on a full SQL Server database and not on a LocalDB database.
Is there any way to tell the Role Provider not to make this stored procedure call without having to write a Role Provider from scratch? I have my database connection defined as follows -
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\DefaultConnection.mdf;Initial Catalog=aspnetdb;Integrated Security=True" providerName="System.Data.SqlClient" />
I have written an alternate solution - a custom RoleProvider that works with LocalDB .
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using MyApp.Models;
namespace MyAppNamespace
{
/// <summary>
/// The purpose of this role provider is to support membership + roles in a MSSQL LocalDB.
/// It works around the limitation of the standard SqlRoleProvider
/// which requires stored procedures in a SQL Server database.
/// </summary>
public class CustomRoleProvider : RoleProvider
{
public override string ApplicationName { get; set; }
public override string[] GetRolesForUser(string username)
{
using (var usersContext = new ApplicationDbContext())
{
var user = usersContext.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));
if (user == null) return new string[] { };
var roles = from ur in user.Roles
from r in usersContext.Roles
where ur.RoleId == r.Id
select r.Name;
if (roles != null)
return roles.ToArray();
else
return new string[] { };
}
}
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
foreach(var userName in usernames)
{
var roles = GetRolesForUser(userName);
foreach(var roleName in roleNames)
{
if (!roles.Contains<string>(roleName))
{
using (var usersContext = new ApplicationDbContext())
{
var user = usersContext.Users.FirstOrDefault(u => u.UserName.Equals(userName, StringComparison.CurrentCultureIgnoreCase));
if (user != null)
{
var role = usersContext.Roles.FirstOrDefault(r => r.Name.Equals(roleName, StringComparison.CurrentCultureIgnoreCase));
if (role != null)
{
var userRole = new Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole();
userRole.RoleId = role.Id;
userRole.UserId = user.Id;
user.Roles.Add(userRole);
}
}
usersContext.SaveChanges();
}
}
}
}
}
public override string[] GetAllRoles()
{
using (var usersContext = new ApplicationDbContext())
{
return usersContext.Roles.Select(r => r.Name).ToArray();
}
}
public override bool IsUserInRole(string username, string roleName)
{
return this.GetRolesForUser(username).Contains(roleName);
}
public override void CreateRole(string roleName)
{
var role = new Microsoft.AspNet.Identity.EntityFramework.IdentityRole();
role.Id = Guid.NewGuid().ToString();
role.Name = roleName;
using (var usersContext = new ApplicationDbContext())
{
usersContext.Roles.Add(role);
usersContext.SaveChanges();
}
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
var usersInRole = GetUsersInRole(roleName);
if (throwOnPopulatedRole)
{
if (usersInRole.Length > 0)
{
throw new Exception("Role " + roleName + " is not empty");
}
}
var roleNameArray = new string[1];
roleNameArray[0] = roleName;
RemoveUsersFromRoles(usersInRole, roleNameArray);
using (var usersContext = new ApplicationDbContext())
{
var role = usersContext.Roles.FirstOrDefault(r => r.Name.Equals(roleName, StringComparison.CurrentCultureIgnoreCase));
if (role != null)
{
usersContext.Roles.Remove(role);
usersContext.SaveChanges();
return true;
}
return false;
}
}
public override bool RoleExists(string roleName)
{
using (var usersContext = new ApplicationDbContext())
{
var role = usersContext.Roles.FirstOrDefault(r => r.Name.Equals(roleName, StringComparison.CurrentCultureIgnoreCase));
return (role != null);
}
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
foreach (var userName in usernames)
{
var roles = GetRolesForUser(userName);
foreach (var roleName in roleNames)
{
if (!roles.Contains<string>(roleName))
{
using (var usersContext = new ApplicationDbContext())
{
var user = usersContext.Users.FirstOrDefault(u => u.UserName.Equals(userName, StringComparison.CurrentCultureIgnoreCase));
if (user != null)
{
var role = usersContext.Roles.FirstOrDefault(r => r.Name.Equals(roleName, StringComparison.CurrentCultureIgnoreCase));
if (role != null)
{
var userRole = new Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole();
userRole.RoleId = role.Id;
userRole.UserId = user.Id;
user.Roles.Remove(userRole);
}
}
usersContext.SaveChanges();
}
}
}
}
}
public override string[] GetUsersInRole(string roleName)
{
using (var usersContext = new ApplicationDbContext())
{
var role = usersContext.Roles.FirstOrDefault(r => r.Name.Equals(roleName, StringComparison.CurrentCultureIgnoreCase));
var users = from ur in role.Users
from u in usersContext.Users
where ur.RoleId == u.Id
select u.UserName;
if (users != null)
return users.ToArray();
else
return new string[] { };
}
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
var regEx = new System.Text.RegularExpressions.Regex(usernameToMatch);
using (var usersContext = new ApplicationDbContext())
{
var role = usersContext.Roles.FirstOrDefault(r => r.Name.Equals(roleName, StringComparison.CurrentCultureIgnoreCase));
var users = from ur in role.Users
from u in usersContext.Users
where ur.RoleId == u.Id
&& regEx.IsMatch(u.UserName)
select u.UserName;
if (users != null)
return users.ToArray();
else
return new string[] { };
}
}
}
}

new password not save in database aftter change password cakephp

Sorry before, may I ask, when change password and forgot password, the new password is not fed stored in the database if my beforeSave function like this :
public function beforeSave($options = array()) {
if (!$this->id && !isset($this->data[$this->alias][$this->primaryKey]) && isset($this->data[$this->alias]['password'])) { $this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']); } else {
unset($this->data[$this->alias]['password']);
}return true;}
But if the function of BeforeSave changed like this
public function beforeSave($options = array()) { $this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);}}
the value of new password is success save to database, but when the user doing edit function and password left empty, password in database has hashing twice
please help me, thanks before
oh yaa, this is my changePassword function :
public function account(){
if(!$this->Session->check('Auth.User')){
$this->Session->setFlash(__('You must be logged in to view this page.'));
return $this->redirect(array('action' => 'login'));
}
//set user's ID in model which is needed for validation
$this->User->id = $this->Auth->user('id');
//load the user (avoid populating $this->data)
$current_user = $this->User->findById($this->User->id);
$this->set('current_user', $current_user);
$this->User->useValidationRules('ChangePassword');
$this->User->validate['re_password']['compare']['rule'] = array('equalToField', 'password', false);
$this->User->set($this->data);
if(!empty($this->data) && $this->User->validates()){
$password = $this->data['User']['password'];
$this->User->saveField('password', $password);
$this->Session->setFlash('Your password has been updated');
$this->redirect(array('action' => 'account'));
}
$this->layout = 'dashboard_admin';
}
Add new form field in edit form, instead of password, add new_password. It will be hashed only if user put somethin in there...
public function edit($id = null) {
$this->User->id = $id;
if (!$this->User->exists()) {
throw new NotFoundException(__('Invalid user'));
}
if ($this->request->is('post') || $this->request->is('put')) {
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('Saved.', true));
$this->redirect(array('action' => 'view', $id));
} else {
$this->Session->setFlash(__('Error.', true));
}
} else {
$user = $this->User->read(null, $id);
$this->request->data = $user;
unset($this->request->data['User']['password']);
$this->set('user', $user);
}
public function beforeSave($options = array()) {
if (!empty($this->data['User']['new_password'])) {
$this->data['User']['password'] = AuthComponent::password($this->data['User']['new_password']);
unset($this->data['User']['new_password']);
}
}
Use this only in case of edit (where user is not changing his password) and you should not show even hashed password to your user
if ($this->request->is('post') || $this->request->is('put')) {
unset($this->request->data['User']['password']);
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('Saved.', true));
$this->redirect(array('action' => 'view', $id));
} else {
$this->Session->setFlash(__('Error.', true));
}
}

Resources