Play framework, Scala: authenticate User by Role - scala

I've user roles: user, manager, admin. I need to authenticate them in controllers (methods). For example only admin can delete (now it looks like this, need to change that only admin should have permission):
def deleteBook(id: Int) = DBAction {
findById(id) match {
case Some(entity) => {
books.filter(_.id === id).delete
Ok("")
}
case None => Ok("")
}
}
I've many controllers and methods. I need to authenticate before process request (for example deleting book). My routes file contains:
...
DELETE /books/:id #controllers.Book.deleteBook(id: Int)
...
Some routes are only accessible to admin and manager. Some are for all types of users.
I'm currently seeing deadbolt2scala authorization module for play.
Can you recommend best way to authenticate multirole users in playframework scala?

I've managed to do this by using StackableControllers provided by https://github.com/t2v/stackable-controller
Basically, I use a basic access control list provided by my application.conf. I start by checking if there is a user in my request. If there is one, I can check if he has sufficient access rights to perform the action.
Such a feature may be implemented using BodyParser composition too. I've never done that, though, so someone else's advice may be better for you.

Related

How can mock a Keycloak token for unit test in ScalaTest

The app I'm working (in Scala) keep a register of all the users that login using Keycloak, for that creates a user for every new keycloak user using the information on the keycloak authorization token
private def decodeToken(token: String): Try[User] = {
Try { AdapterTokenVerifier.verifyToken(token, keycloakDeployment) } match {
case Success(accessToken: AccessToken) =>
Try (User(name = accessToken.getName, userName = accessToken.getPreferredUsername, email = Some(accessToken.getEmail))
case Failure(ex) =>
throw new Exception("authentication failed")
}}
My problem is the following, I need to do unit tests to my code, for that is used ScalaTest, how can a mock a token of keycloak inside the test so this code can be tested.
I already write the test and works but I have to pass manually a token, that means, any other time I run the test if the token expire the test files, also files if the test is run in another computer, also fails in gitlab (don't pass pipelines)
Note: I'm new with scala and with keycloak, also English is not my firs language, so it's understandable that some things are not very clear, if you think you can help me, feel free to ask anything
Note 2: With that user that is created with the Keycloak token, other things are done but this is the most relevant to my problem

provide /me API endpoints while still providing basic endpoints matching the same routes

I want to provide a me endpoint because not everyone should have access to all resources. The current logged in user should only have access to his own resources. So let's assume you would have a simple Todo REST API with an endpoint returning all tasks from a single user
GET /users/{username}/tasks
The current logged in user should not be able to get information about other users. A possible solution for this would be
GET /users/me/tasks
This has already been discussed here
Designing URI for current logged in user in REST applications
The problem is that I also want to keep the endpoint from above for development purposes (a private/hidden endpoint). Unfortunately both endpoints match the same route. So me could be the username.
I don't want to prevent a username called me with an if-statement like
if(username == "me")
throw new ConflictException("Username 'me' is forbidden");
I think this would be bad design. A possible solution would be to avoid embedding me in a resource and instead embed the resources in me endpoints. So instead of
GET /users/me/tasks
GET /users/me/orders
POST /users/me/tasks
PATCH /users/me/username
DELETE /users/me/tasks/{taskName}
I could remove the users resource and make me the initial base resource. As you might guess /users/:username extracts the username from the url parameter and /me extracts the username from the json web token but both endpoints run the same logic.
So when I want to keep private/hidden endpoints to fetch a user by username do I have to make me a separate resource? Or is there a way to keep me only as a replacement for the username parameter?
EDIT
I tried to create a simple route example. As you can see most of the endpoints of users and me are mirrored and only differ by the user identification (username as parameter or jwt payload). And for this sample only the me endpoints are accessible to everyone. Other endpoints are private / hidden.
users
GET / => get users => private
GET /:username => get user => private
GET /:username/tasks => get tasks from user => private
GET /:username/tasks/:taskName => get task from user => private
POST /:username/tasks => create user task => private
PATCH /:username/username => update username => private
DELETE /:username => delete user => private
DELETE /:username/tasks/:taskName => delete user task => private
tasks
GET / => get tasks => private
me
GET / => get current logged in user => public
GET /tasks => get tasks from current logged in user => public
GET /tasks/:taskName => get task from current logged in user => public
POST /tasks => create task for current logged in user => public
PATCH /username => update username => public
DELETE / => delete current logged in user => public
DELETE /tasks/:taskName => delete task from current logged in user => public
I think you are getting confused because your definitions are a bit tangled.
Here's the good news: REST doesn't care about the spelling conventions you use for your identifiers. So if it makes your life easy to have
GET /users/12345/tasks
GET /me/tasks
Then you can implement both of those, make sure that the access restrictions are correct, and off you go.
There's nothing wrong, from a REST perspective, about using
GET /users/12345/tasks
GET /users/me/tasks
The spellings of the target-uri are different, which means that from the perspective of a general-purpose client they identify different resources.
BUT... the routing implementation that you are using to implement your API may not allow you to easily distinguish these two cases. What you have effectively got here are two different matches for the same pattern, which requires some extra clever to remove the ambiguity; if your routing library/framework doesn't offer that, then you are going to have to shim it in yourself.
And, as you note, you've got a huge mess if the token "me" itself could match a real identifier.
REST doesn't have a concept of "base resource" - at least, not one that lines up with what you are thinking about. There's no inherent relationship between /users and /users/12345 from the REST perspective. Many server frameworks do treat request handling as a hierarchy of "resources", but again: that's an implementation detail of your particular server.
REST really only cares about your interface - that you understand HTTP requests in the standard way.
Which all brings us back to the good news; since REST doesn't care what spelling conventions you use, you are welcome to make any arbitrary choices you like... including whatever spelling convention makes your internal routing framework easy to work with.
So if your routing framework is telling you to create a different "initial base resource" to support your family of "me" endpoints, then just do that.

Rails - How to authorise user with third party api

I'm setting up some authentication in my rails application. Only thing is I want to log in a user based on their credentials with another API.
The application will have to send a POST request with their username and password in the body to the API and if the request is successful then the user authorised.
I'm having trouble trying to do this with devise, I'm just looking for tips you guys have in order to implement this.
Thanks!
Devise allows you to define custom strategies for authentication. You can therefore create a new strategy to handle it. Database Authentication is one of the strategy already defined at Devise. You can check the source here
A rough idea of your strategy could like this.
Create a file at config/initializers/external_authenticatable.rb and define the strategy
require 'devise/strategies/database_authenticatable'
module Devise
module Strategies
class ExternalAuthenticatable < DatabaseAuthenticatable
def authenticate!
resource = password.present? && mapping.to.find_for_database_authentication(authentication_hash)
if validate(resource){ valid_credentials?(resource) }
remember_me(resource)
resource.after_database_authentication
success!(resource)
end
fail(:not_found_in_database) unless resource
end
def valid_credentials?(resource)
request_params = { email: resource.email, password: password }
# Make your post request here and return true false using authentication_hash
end
end
end
end
Now we need to inform devise that we want to use this strategy first before any other defaults. This can be done by editing /config/initializers/devise.rb
config.warden do |manager|
manager.strategies.add(:external, Devise::Strategies::ExternalAuthenticatable)
manager.default_strategies(:scope => :user).unshift :external
end
Restart your Rails application and you are done.

securesocial redirect user on save

I'm trying to make authentication work with securesocial plugin for Play! framework 2.2.
I'm implementing my own version of UserService.
When someone logs into my website for the first time using some identity (Facebook, Twitter or everything else) I want to redirect him to a Registration Page to collect more informations about him and to make him accept my Terms and Conditions.
How can I achieve this?
Thank you for your help.
A.M.
def index = SecuredAction { implicit request =>
// Check if user is logging in for the first-time... or other logic
if(true){//If logic for redirection meets
Redirect(routes.Application.step2)
}else{
println("Home")
Ok(views.html.index(request.user))
}
}
def step2 = SecuredAction { implicit request =>
//Gather Other data... point to another form, your choice
Ok(views.html.step2(request.user))
}
I presume your files look like these..
securesocial.conf
securesocial {
onLoginGoTo=/
routes.conf
GET / controllers.Application.index
GET /step2 controllers.AmazonController.step2 //Additional Route

"su" Equivalent for Web Application Auth, Design Question

I develop and maintain a customer portal, written in Perl/Catalyst. We make use of the Catalyst authentication plugins (w/ an LDAP storage backend, coupled with a few deny_unless rules to ensure the right people have the right group membership).
It's often that in managing a customer's permissions, we have the need to test out a user's settings before we hand things over. Currently, our only recourse is to reset a user's password and log in ourselves, but this is less than ideal, particularly if the user has already set their own passwords, etc.
My question is this: for Catalyst, has anyone come across a method of impersonating a user account such that, given the correct super-admin privileges, one could impersonate another account temporarily while testing out a setting, and then back out once done?
If not in Catalyst, then how have people approached this in other frameworks, or their own custom solutions? Admittedly, this is something that introduces a potentially egregious attack vector for a web application, but if forced to implement, how have people approached design for this? Perhaps some serious cookie-session-fu? Or possibly an actualID/effectiveID system?
We use a custom authenticator controller, a custom user class (MyApp::Core::User) and several realms:
package MyApp::Controller::Auth;
...
sub surrogate : Local {
my ( $self, $c ) = #_;
my $p = $c->req->params;
my $actual_user = $c->user; # save it for later
try {
$c->authenticate({ id=>$p->{surrogate_id} }, 'none');
$c->session->{user} = new MyApp::Core::User(
active_user => $actual_user,
effective_user => $c->user );
$c->stash->{json} = { success => \1, msg => "Login Ok" };
} catch {
$c->stash->{json} = { success => \0, msg => "Invalid User" };
};
$c->forward('View::JSON');
}
In myapp.conf I use something like this:
<authentication>
default_realm ldap
<realms>
<ldap>
# ldap realm config stuff here
</local>
<none>
<credential>
class Password
password_field password
password_type none
</credential>
<store>
class Null
</store>
</none>
</realms>
</authentication>
That way we're creating a normal Catalyst user object, but wrapping it around our custom user class for more control. I probably could have created an specialized realm for surrogating, but I've chosen using my own user class instead. It was done a while back and I can recall why we did it that way.