Skip to content
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

Merged
merged 1 commit into from
Sep 13, 2018

Conversation

ArnaudValensi
Copy link
Contributor

@ArnaudValensi ArnaudValensi commented Sep 3, 2018

To test

Use feature/2fa-rails on the front, and errors will be better with feature/2fa-rails on the server.

and set the premiumTwoFactor in the project model.

Litterature

Wrapped exceptions: https://pupeno.com/2014/02/05/wrapped-exceptions-in-ruby/

Pull Request checklist:

  • Write an explicit title for the Pull Request
  • Write changes made in the CHANGELOG.md
  • Create automatic tests
  • No automatic tests failures
  • Test manually the implemented changes
  • Review my own code (indentation, syntax, style, simplicity, readability)
  • Wonder if you can improve the existing code

@ArnaudValensi ArnaudValensi self-assigned this Sep 3, 2018
@ArnaudValensi
Copy link
Contributor Author

@ArnaudValensi ArnaudValensi force-pushed the feature/2fa-rails branch 21 times, most recently from 27ed514 to 52675df Compare September 6, 2018 15:06
@@ -1,3 +1,3 @@
module ForestLiana
VERSION = "2.11.11"
Copy link
Contributor Author

@ArnaudValensi ArnaudValensi Sep 6, 2018

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.

@ArnaudValensi ArnaudValensi force-pushed the feature/2fa-rails branch 2 times, most recently from 7381d31 to 4528e8d Compare September 6, 2018 15:43
recursively_print(self)
end

def recursively_print(error, margin="")
Copy link
Contributor Author

@ArnaudValensi ArnaudValensi Sep 6, 2018

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 }])
Copy link
Contributor Author

@ArnaudValensi ArnaudValensi Sep 6, 2018

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.

Copy link
Contributor

@arnaudbesnier arnaudbesnier left a 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.
Copy link
Contributor

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'
Copy link
Contributor

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) };
Copy link
Contributor

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')
Copy link
Contributor

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
Copy link
Contributor

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) };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{ token: create_token(...) }

def initialize(message = "Unauthorized")
super(401, :unauthorized, message)
end
end
end
end
Copy link
Contributor

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:
screen shot 2018-09-07 at 11 14 59

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed:
capture d ecran 2018-09-10 a 10 59 22

And about the trace, it is in debug, this mean if it appears it is because someone want to see a maximum of informations.

FOREST_LOGGER.info "#{margin}which was caused by:"
recursively_print(error.cause, "#{margin} ")
else
FOREST_LOGGER.debug error.backtrace.join("\n")
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've improved and I've decided too also display the errors sent by the server to have a maximum of presision.

Here for permissions:
Server down
capture d ecran 2018-09-11 a 16 06 31

Bad secret
capture d ecran 2018-09-11 a 16 25 22

Here for authentication:
capture d ecran 2018-09-11 a 16 18 01
capture d ecran 2018-09-11 a 16 19 23

use_google_authentication: false,
rendering_id: rendering_id,
project_id: project_id,
auth_data: { :email => email, :password => password },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

screen shot 2018-09-11 at 07 07 52

foo: is just a simpler syntax for :foo =>

end
route

raise Errors::HTTP401Error.new(error_message) if error_message
Copy link
Contributor

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.


# 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:"
Copy link
Contributor

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?

Copy link
Contributor Author

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.

@ArnaudValensi ArnaudValensi force-pushed the feature/2fa-rails branch 2 times, most recently from 4e8f686 to 35d10c2 Compare September 12, 2018 15:11
@ArnaudValensi ArnaudValensi force-pushed the feature/2fa-rails branch 2 times, most recently from d7f6515 to f3f1515 Compare September 12, 2018 16:00
@ArnaudValensi ArnaudValensi merged commit 6d22f38 into devel Sep 13, 2018
@arnaudbesnier arnaudbesnier deleted the feature/2fa-rails branch January 30, 2019 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants