神の愛のためにSecurityUtils
クラスを作らないでください!
あなたのクラスはたった数ヶ月でスパゲッティコードの1万行になります!あなたは持っている必要がありますAction
(作成、読み取り、更新、破壊、リストなど)をお使いに渡されるタイプをisUserAuthorized()
すばやく千行長になる方法、switch
ユニットテストには難しいだろう、ますます複雑なロジックとの声明。しないでください。
一般に、少なくともRuby on Railsでは、各モデルにポリシークラスを設定することにより、各ドメインオブジェクトに独自のアクセス権限を与えるようにします。次に、コントローラーは、要求の現在のユーザーがリソースにアクセスできるかどうかをポリシークラスに尋ねます。Rubyでの例は次のとおりです。Javaでこれほど実装したことがないので、アイデアは明確に出てくるはずです。
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
複雑なネストされたリソースがある場合でも、一部のリソースはネストされたリソースを「所有」する必要があるため、トップレベルのロジックは本質的にバブルダウンします。ただし、ネストされたリソースは、「親」リソースとは関係なく更新できる場合に、独自のポリシークラスを必要とします。
私の大学の学部向けのアプリケーションでは、すべてがCourse
オブジェクトを中心に展開します。これはUser
中心的なアプリケーションではありません。ただし、User
はに登録されているCourse
ため、次のことを簡単に確認できます。
@course.users.include? current_user && (whatever_other_logic_I_need)
User
ほとんどすべてのリソースがに関連付けられているため、特定のリソースを変更する必要がありますCourse
。これは、owns_whatever
メソッドのポリシークラスで行われます。
私はあまりJavaアーキテクチャーを実行していませんPolicy
が、認証を必要とするさまざまなリソースがインターフェースを実装する必要があるインターフェースを作成できるようです。次に、ドメインオブジェクトごとに必要となるほど複雑になる可能性のあるすべての必要なメソッドがあります。重要なことは、ロジックをモデル自体に結び付けることですが、同時にそれを別のクラス(単一責任原則(SRP))に保持します。
コントローラのアクションは次のようになります。
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);
}