For the first question, your approach seems correct to me. You consume a token in the sendsms
entry that can only be generated by the login
endpoint. You could also use a generic token created by login, and not easily guessable (it would be equivalent to a cryptographically generated session ID), used as primary key to a database tuple. There you can store whatever you want (for example the number of SMS still allowed to be sent).
You can also store the "state" in a variable and protect it using, say, HMAC, continuously exchanging it between the server and the client, but you still require some server state to defend against replay attacks on non-idempotent endpoints; at minimum, you need a per-session incremental counter.
The second problem is trickier, and you need first to ask yourself what the difference in behaviour (as seen by the server) would be between the app with its logic, and a malicious user sending repeated requests. You might enforce a minimum delay between requests that is comparable to the maximum rate at which the app can be driven, so that there's no longer an advantage in using tricks rather than going through the app.
Please for the love of God do not create a SecurityUtils
class!
Your class will become 10k lines of spaghetti code in a matter of months! You would need to have an Action
type (create, read, update, destroy, list, etc.) passed into your isUserAuthorized()
method, which would quickly become a thousand-line-long switch
statement with increasingly complex logic that would be difficult to unit test. Don't do it.
Generally what I do, at least in Ruby on Rails, is have each domain object be responsible for its own access privileges by having a policy class for each model. Then, the controller asks the policy class if the current user for the request has access to the resource or not. Here's an example in Ruby, since I've never implemented anything like it in Java, but the idea should come across cleanly:
class OrderPolicy
class Scope < Struct.new(:user, :scope)
def resolve
# A user must be logged in to interact with this resource at all
raise NotAuthorizedException unless user
# Admin/moderator can see all orders
if (user.admin? || user.moderator?)
scope.all
else
# Only allow the user to see their own orders
scope.where(orderer_id: user.id)
end
end
end
# Constructor, if you don't know Ruby
def initialize(user, order)
raise NotAuthorizedException unless user
@user = user
@order= order
end
# Whitelist what data can be manipulated by each type of user
def valid_attributes
if @user.admin?
[:probably, :want, :to, :let, :admin, :update, :everything]
elsif @user.moderator?
[:fewer, :attributes, :but, :still, :most]
else
[:regualar, :user, :attributes]
end
end
# Maybe restrict updatable attributes further
def valid_update_attributes
end
# Who can create new orders
def create?
true # anyone, and they would have been authenticated already by #initialize
end
# Read operation
def show?
@user.admin? || @user.moderator? || owns_order
end
# Only superusers can update resources
def update?
@user.admin? || @user.moderator?
end
# Only admins can delete, because it's extremely destructive or whatever
def destroy?
@user.admin?
end
private
# A user 'owns' an order if they were the person who submitted the order
# E.g. superusers can access the order, but they didn't create it
def owns_order
@order.orderer_id == @user.id
end
end
Even if you have complex nested resources, some resource must 'own' the nested resources, so the top-level logic inherently bubbles down. However, those nested resources need their own policy classes in the event that they can be updated independently of the 'parent' resource.
In my application, which is for my university's department, everything revolves around the Course
object. It's not a User
-centric application. However, User
s are enrolled in Course
, so I can simply ensure that:
@course.users.include? current_user && (whatever_other_logic_I_need)
for any resource that a particular User
needs to modify, since almost all resources are tied to a Course
. This is done in the policy class in the owns_whatever
method.
I haven't done much Java architecture, but it seems that you could make a Policy
interface, where the different resources that need to be authenticated must implement the interface. Then, you have all the necessary methods which can become as complex as you need them to be per domain object. The important thing is to tie the logic to the model itself, but at the same time keep it in a separate class (single responsibility principle (SRP)).
Your controller actions could look something like:
public List<Order> index(OrderQuery query) {
authorize(Order.class)
// you should be auto-rescuing the NotAuthorizedException thrown by
//the policy class at the controller level (or application level)
// if the authorization didn't fail/rescue from exception, just render the resource
List<Order> orders = db.search(query);
return renderJSON(orders);
}
public Order show(int orderId) {
authorize(Order.class)
Order order = db.find(orderId);
return renderJSON(order);
}
Best Answer
Yes, your approach is OK and commonly used.
In fact, if you model your code right, you are likely to be using the same code for access authorization for both cases anyway.
Very simplified example: