-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[+] Authentication - Add two factor authentication on forest-rails #285
Conversation
27ed514
to
52675df
Compare
lib/forest_liana/version.rb
Outdated
@@ -1,3 +1,3 @@ | |||
module ForestLiana | |||
VERSION = "2.11.11" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will remove the commit with this change before merging. But with this it should work out of the box.
52675df
to
7a66900
Compare
7381d31
to
4528e8d
Compare
config/initializers/errors.rb
Outdated
recursively_print(self) | ||
end | ||
|
||
def recursively_print(error, margin="") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will display recursively all the resons and then the stack trace of the deeper exception (since the higher stacks are contained in the deepest).
Example:
Cannot reach the forest server
which was caused by:
Unauthorized
...then all stacktrace...
end | ||
rescue ForestLiana::Errors::ExpectedError => error | ||
error.display_error | ||
error_data = JSONAPI::Serializer.serialize_errors([{ detail: error.message }]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only the higher exception message is returned, so we just need to catch and rethrow to hide a sensitive exception message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The feature works well.
CHANGELOG.md
Outdated
@@ -1,6 +1,8 @@ | |||
# Change Log | |||
|
|||
## [Unreleased] | |||
### Added | |||
- Authentication - Add two factor authentication on forest-rails. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are in the Forest Rails liana, not necessary to specify this here ;)
Authentication - Add two factor authentication
config/routes.rb
Outdated
post 'sessions' => 'sessions#create' | ||
post 'sessions-google' => 'sessions#create_with_google' | ||
post 'sessions' => 'sessions#login_with_password' | ||
post 'sessions-google' => 'sessions#login_with_google' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a session object creation so sessions#create
was a better wording in the REST logic.
=> create_with_password
end | ||
end | ||
|
||
return { 'token' => create_token(user, @rendering_id) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ token: create_token(user, @rendering_id) }
?
(without trailing semicolon)
FOREST_LOGGER.error 'Cannot use the two factor authentication because the environment variable "FOREST_2FA_SECRET_SALT" is not set.' | ||
FOREST_LOGGER.error 'You can generate it using this command: `$ openssl rand -hex 10`' | ||
|
||
raise ForestLiana::Errors::HTTP401Error.new('Invalid 2FA configuration, please ask more information to your admin') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you already are in the ForestLiana
module, I guess you can simplify with raise Errors::HTTP401Error.new('...')
end | ||
route | ||
|
||
response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def self.check_configuration_errors(response)
if response.is_a?(Net::HTTPNotFound)
error_message = 'Cannot retrieve the data from the Forest server. Can you check that you properly copied the Forest envSecret in the Liana initializer?'
elsif response.is_a?(Net::HTTPUnprocessableEntity)
error_message = 'Cannot retrieve the project you\'re trying to unlock. The envSecret and renderingId seems to be missing or inconsistent.'
end
raise Errors::HTTP401Error.new(error_message) if error_message
response
end
looks simpler to me
TwoFactorRegistrationConfirmer.new(@project_id, @use_google_authentication, @auth_data) | ||
.perform | ||
|
||
return { 'token' => create_token(user, @rendering_id) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ token: create_token(...) }
config/initializers/errors.rb
Outdated
def initialize(message = "Unauthorized") | ||
super(401, :unauthorized, message) | ||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another case, on TOTP token submit if Forest Server is down. Here is the logs we have:
The technical error is not really interesting, the only thing we should say is that the service didn't work because Forest looks to be down or in maintenance. Providing traces will make the developer find the reason for the crash.
But he/she will work for nothing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, in this case I agree, I will improve this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
d9bb270
to
79faed0
Compare
config/initializers/errors.rb
Outdated
FOREST_LOGGER.info "#{margin}which was caused by:" | ||
recursively_print(error.cause, "#{margin} ") | ||
else | ||
FOREST_LOGGER.debug error.backtrace.join("\n") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it suppose to handle Ruby < 2.1.0 case? It looks like it prints something at the end of the recursion too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's good, if we use ruby < 2.1.0, we display the exception message and the stack trace.
body: body.to_json, | ||
}).response | ||
|
||
check_configuration_errors(response) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't be the same pattern here with a begin/rescue
like you did in the self.get
?
raise "Cannot retrieve the data from the Forest server. An error occured in Forest API." | ||
end | ||
rescue | ||
raise Errors::HTTP401Error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would the permission getter send a 401 if some internal error occurs on Forest Server?
It seems that this error will be rescued in the resources_controller and sent an internal error (500) if I read well the code. I am not sure we really want that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use_google_authentication: false, | ||
rendering_id: rendering_id, | ||
project_id: project_id, | ||
auth_data: { :email => email, :password => password }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
end | ||
route | ||
|
||
raise Errors::HTTP401Error.new(error_message) if error_message |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a generic service, I am not sure it is a good idea to respond with a 401. Depending on the usage not sure we want to disconnect the end user.
config/initializers/errors.rb
Outdated
|
||
# NOTICE: Ruby < 2.1.0 doesn't have `cause` | ||
if error.respond_to?(:cause) && !error.cause.nil? | ||
FOREST_LOGGER.info "#{margin}which was caused by:" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not FOREST_LOGGER.error
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because if a user us not authorized, it is not an error in the perspective of the liana server. This is like the expected/unexpected errors in forestadmin-server.
4e8f686
to
35d10c2
Compare
d7f6515
to
f3f1515
Compare
To test
Use
feature/2fa-rails
on the front, and errors will be better withfeature/2fa-rails
on the server.and set the
premiumTwoFactor
in theproject
model.Litterature
Wrapped exceptions: https://pupeno.com/2014/02/05/wrapped-exceptions-in-ruby/
Pull Request checklist: