I am using grails 2.3.0 and facing the weird issue when encoding the password with spring security:
This is my method to encode password:
String encodePassword(String password) {
return springSecurityService.encodePassword(password)
}
and using like that
log.debug encodePassword("mkb")
log.debug encodePassword("mkb")
log.debug encodePassword("mkb")
I am encoding the same password several times and each time I am getting the different encoded password.
logs:
$2a$10$h8T4BxgOeozmH/VSPJl7NeTaF2P0iONpSdqDN7dDFFAG.sy8WG/8K
$2a$10$a7qybaiLF/eNrTSwFohjkezNaJTTDdMEinRYKjxDzEt.OoxaIgFOu
$2a$10$nZVhUT0QTmmbtt22CPtM..cLxU252RGBIMkd5aSd2AFXNTNLQ./6u
That's fine. Looks like you're using BCrypt password hash, this algorithm uses random salt each time you encode password (other hashing algorithms use a 'salt source property', like id). This salt is prepended to hash
So you have:
$2a - salt version
$10 - rounds
$h8T4BxgOeozmH/VSPJl7NeTaF2P0iONpSdqDN7dDFFAG.sy8WG/8K - Base64 for salt+hash, where salt get first 24 characters, and hash takes the rest:
h8T4BxgOeozmH/VSPJl7NeTaF - salt
2P0iONpSdqDN7dDFFAG.sy8WG/8K - hash (10 rounds for salt + password)
See Spring Security's BCrypt sources: https://github.com/spring-projects/spring-security/blob/master/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java
If you need to check user password manually, you have to use passwordEncoder, like:
//dependency injection
def passwordEncoder
//validate
String enteredPassword = params.password
User user = ...
if (!passwordEncoder.isPasswordValid(user.password, enteredPassword, null)) { //validates raw password against hashed
//... wrong password entered
}
Grails spring security describes the process of updating a password at: https://grails-plugins.github.io/grails-spring-security-core/4.0.x/index.html
User user = User.findByUsername(username)
if (!passwordEncoder.matches(password, user.password)) {
flash.message = 'Current password is incorrect'
render view: 'passwordExpired', model: [username: session['SPRING_SECURITY_LAST_USERNAME']]
return
}
if (passwordEncoder.matches(password_new, user.password)) {
flash.message = 'Please choose a different password from your current one'
render view: 'passwordExpired', model: [username: session['SPRING_SECURITY_LAST_USERNAME']]
return
}
Where password or new_password are parameters or method arguments
Related
I'm pretty new to Rails but we have an app using Devise and Omniauth for authentication and have recently integrated Omniauth-Saml by following the Omniauth documentation for Devise integration: https://github.com/omniauth/omniauth-saml#devise-integration The authentication works and we can create users and use these accounts without any issues.
In the SAML response attributes is an lacode (4-digit string). We want to check this user attribute against a reference lacode. If their cag matches the reference cag we want to set the verified_at attribute in the user.rb model.
I've updated the user model and to test if I set the oauth_lacode to "9064" to match the oauth_lacode_ref then the code works and the user's verified_at time and date are set at point of account creation.
app/models/user.rb
# Get the existing user by email if the provider gives us a verified email.
def self.first_or_initialize_for_oauth(auth)
oauth_email = auth.info.email
oauth_email_confirmed = oauth_email.present? && (auth.info.verified || auth.info.verified_email)
oauth_lacode = auth.extra.raw_info.lacode
oauth_lacode_ref = "9064"
oauth_lacode_confirmed = oauth_lacode == oauth_lacode_ref
oauth_user = User.find_by(email: oauth_email) if oauth_email_confirmed
oauth_user || User.new(
username: auth.info.name || auth.uid,
email: oauth_email,
oauth_email: oauth_email,
password: Devise.friendly_token[0, 20],
terms_of_service: "1",
confirmed_at: oauth_email_confirmed ? DateTime.current : nil,
verified_at: oauth_lacode_confirmed ? DateTime.current : nil
)
end
I'm not mapping and calling the lacode from the hash correctly as I see this error in the log "NoMethodError (undefined method `lacode' for #OneLogin::RubySaml::Attributes:0x00007f7a5040ad40):"
This is how I'm mapping the attributes in config/initializers/devise.rb
attribute_statements: { email: ['urn:oid:0.9.2342.19200300.100.1.22'],
lacode: ['urn:oid:0.9.2342.19200300.100.1.17']}
I have confirmed with the IDP that 'urn:oid:0.9.2342.19200300.100.1.17' is mapped to the lacode in the SAML response.
As in the User model above, this is how I'm trying to access the lacode from within the User model.
"saml_cag = auth.extra.raw_info.lacode"
This is the guidance from Omniauth Saml:
:attribute_statements - Used to map Attribute Names in a SAMLResponse
to entries in the OmniAuth info hash. For example, if your
SAMLResponse contains an Attribute called 'EmailAddress', specify
{:email => ['EmailAddress']} to map the Attribute to the corresponding
key in the info hash. URI-named Attributes are also supported, e.g.
{:email =>
['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']}.
Note: All attributes can also be found in an array under
auth_hash[:extra][:raw_info], so this setting should only be used to
map attributes that are part of the OmniAuth info hash schema.
Does this sentence at the end mean I don't need to/can't map the attribute. Can anyone help or point me in the right direction?
I managed to get this working. Only attribute names specified in the Omniauth Hash Schema can be used.
Mapping the lacode to description in the attribute statement, I was able to access it using "auth.info.description"
I have looked for an answer in an existing question, but found nothing which covers this hashing scheme in Rails.
I am currently migrating a PHP project over to Ruby-on-Rails, which I am still learning. I am struggling to port over an MD5 (I know, it's only temporary...) hashing scheme for hashing user passwords. I have plain MD5 working, but cannot find the correct syntax for the hashing scheme I need.
The mechanics of the hashing scheme in the PHP project are:
Create MD5 hash of password.
Create MD5 hash of salt.
Concatenate password_hash with salt_hash.
Create MD5 hash of concatenated_string.
Compare stored_hash to concatenated_string
The PHP for the hash creation is:
function fn_generate_salted_password($password, $salt)
{
$_pass = '';
if (empty($salt)) {
$_pass = md5($password);
} else {
$_pass = md5(md5($password) . md5($salt));
}
return $_pass;
}
The (pathetic) attempt I have for this section in Rails so far is:
Spree::User.class_eval do
def valid_password?(password)
if self.salt.present
if ::Digest::MD5.hexdigest((::Digest::MD5.hexdigest(password)).(::Digest::MD5.hexdigest(salt))) == self.stored_hash
*# Do some stuff*
else
false
end
end
end
end
Any ideas?
I would do something like this:
def valid_password?(password)
secret = if salt.present?
[password, salt].map { |part| ::Digest::MD5.hexdigest(part) }.join
else
password
end
stored_hash == ::Digest::MD5.hexdigest(secret)
end
I am building a small API that uses basic authentication. What I have done, is that a user can generate a username and password, that could be used to authenticate to the API.
However I have discovered that it is not working 100% as intended. It appears that a request will slip through even though the username is not matching the password. However it will be blocked if the password is incorrect.
Example:
Username: foo
Password: bar
Works:
curl -u foo:bar http://api/protected
Works - But should not:
curl -u f:bar http://api/protected
Does not work - As intended:
curl -u foo:b http://api/protected
To me it appears that it only validates the password, and ignores the username. Here are some code.
# Authentication
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
#user = User.includes(:keys).find_by( :keys => { :api_key => password } )
#user.keys.each do |key|
username == key.api_id && password == key.api_key
end
end
end
#user.keys.each do |key|
username == key.api_id && password == key.api_key
end
This piece of code returns a value of .each, which is the collection it's called on (#user.keys in this case). As it is a truthy value, the check will pass always, regardless of what are the results of evaluating your conditional in the block.
What you intended to ask is "Do any of user's keys match these credentials?". Use the appropriate method, Enumerable#any?
#user.keys.any? do |key|
username == key.api_id && password == key.api_key
end
Documentation: Enumerable#any?
Try this
#user = User.includes(:keys).find_by( :keys => { :api_key => password, :api_id => username } )
This way you'll make sure to find a user with valid both username and password, and you won't need the second line (iterating over keys).
The reason for that is: iterating over a block returns this block, not the value/values inside. So your authenticator found a user with a good password, iterated over his keys (which changed nothing) and returned #user.keys, which evaluates to true. Thus the authentication passed.
How can l implement a hash function (SHA512) that encrypt the username and check if this username and password is correct in the SQL server?
Working login right now if you set a cleartext username
user = User.find_by(username: params[:session][:username].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to :root
else
flash.now[:warning] = 'Wrong username/password'
render 'new'
end
I have tried encrypt the username before it runs the if statement but I can get it to work.
The way I hash the username is:
username = params[:session][:username].downcase
username_encrypted = Digest::SHA2.new(512).hexdigest(username)
any ideas how this can be made?
What i am trying to achieve with this is. Too have one column in SQL with encrypted usernames and another column with "public" usernames. Where public usernames is visible for everybody on the site and encrypted usernames is stored in SQL only for authentication.
I'm using django-1.4.3 and the superuser can create users and assign password to them. Here I'm using django-email-as-username to enable users to login with their email as username. When the superuser adds a new user, the newly added user should be notified through email with his username and password.
I'm able to send email after user creation using post_save signal. But I couldn't get the password as it will be encrypted and stored. I want to email the user, the raw password. How can I achieve this?
I have achieved it using the code below:
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
#receiver(post_save, sender = User)
def my_callback(sender, **kwargs):
import inspect
records =[]
for frame_record in inspect.stack():
records.append(frame_record[3])
if frame_record[3]=='get_response':
request = frame_record[0].f_locals['request']
email = request.POST.get('email')
password1 = request.POST.get('password1')
password2 = request.POST.get('password2')
if email != None and password1 != None and password2 != None and password1 == password2:
html_content ="Hi,<br> Your username: %s <br> Password: %s"
from_email = settings.DEFAULT_FROM_EMAIL
message = EmailMessage('Welcome', html_content %(email, password1), from_email, [email])
message.content_subtype = "html" # Main content is now text/html
message.send()
break