docs/authorization-policies.md in hyper-mesh-0.5.2 vs docs/authorization-policies.md in hyper-mesh-0.5.3
- old
+ new
@@ -1,11 +1,15 @@
### HyperMesh Authorization Policies
-Each time an ActiveRecord model changes, HyperMesh broadcasts the changed attributes over *channels*.
+Access to your models is controlled by *Policies* that describe how the current *acting_user* and *channels* may access your models.
-An application can have several channels and each channel and each active record model can have different *policies* to determine which attributes are sent when a record changes.
+Each browser session has an *acting_user* (which may be nil) and you will define `create`, `update`, and `destroy` policies giving (or denying) the `acting_user` the ability to do these operations.
+Read and *broadcast* access is defined based on *channels* which are connected based again on the current `acting_user`. Read access is initiated when a specific browser tries to read a record attribute, and broadcasts are initiated whenever a model changes.
+
+An application can have several channels and each channel and each active record model can have different policies to determine which attributes are sent when a record changes.
+
For example a Todo application might have an *instance* of a channel for each currently logged in user; an instance of a channel for each team if that team has one or more logged in users; and a general `AdminUser` channel shared by all administrators that are logged in.
Lets say a specific `Todo` changes, which is part of team id 123's Todo list, and users 7 and 8 who are members of that team are currently logged in as well as two of the `AdminUsers`.
When the `Todo` changes we want all the attributes of the `Todo` broadcast on team 123's channel, as well on the `AdminUser`'s channel. Now lets say User 7 sends User 8 a private message, adding a new record to the `Message` model. This update should only be sent to user 7 and user 8's private channels, as well as to the AdminUser channel.
@@ -18,12 +22,12 @@
# class to be treated as a channel.
# The policy is defined by a block that is executed in the context of the
# current acting_user.
- # For our User instance connection the policy is that there must be logged in
- # user, and the connection is made to that user:
+ # For our User instance connection the policy is that there must be a
+ # logged-in user, and the connection is made to that user:
regulate_instance_connections { self }
# If there is no logged in user self will be nil, and no connection will be
# made.
end
@@ -204,11 +208,11 @@
Typically connections are made to ActiveRecord models, and if those are in the `app/models/public` folder everything will work fine.
#### Acting User
-HyperMesh uses the same `acting_user` method that reactive-record permissions uses. This method is typically defined in the ApplicationController and would normally pick up the current session user, and return an appropriate object.
+HyperMesh looks for an `acting_user` method typically defined in the ApplicationController and would normally pick up the current session user, and return an appropriate object.
```ruby
class ApplicationController < ActiveController::Base
def acting_user
@acting_user ||= session[:current_user_id] && User.find_by_id(session[:current_user_id])
@@ -403,10 +407,63 @@
end
regulate_broadcast { |policy| policy.send_all.to(policy.teams) }
end
```
+#### Browser Initiated Change policies
+
+To allow code in the browser to create, update or destroy a model, there must be a change access policy defined for that operation.
+
+Each change access policy executes a block in the context of the record that will be accessed. The current value of `acting_user` is also defined for the life of the block.
+
+If the block returns a truthy value access will be allowed, otherwise if the block returns a falsy value or raises an exception, access will be denied.
+
+In the below examples we assume that your user model responds to `admin?` but this is not built into HyperMesh.
+
+```ruby
+class TodoPolicy
+ # allow creation to any logged in user
+ allow_create { acting_user }
+ # only allow the owner, author any any admin to update a todo
+ allow_update { acting_user == owner || acting_user == author || acting_user.admin? }
+ # don't allow Todo's to be destroyed
+ # this is the default behavior so its not actually needed
+ allow_destroy { false }
+end
+```
+
+There are several variants of the access policy method:
+
+```ruby
+class ConfigDataPolicy
+ allow_change(on: [:create, :update, :destroy]) { acting_user.admin? }
+ # which can be shortened to:
+ allow_change { acting_user.admin? }
+end
+```
+
+```ruby
+class ApplicationPolicy
+ # do any thing to all models unless we are in production! Be careful!
+ allow_change(to: :all) { true } unless Rails.env.production?
+ # and always allow admins to destroy models globally:
+ allow_change(to: :all, on: :destroy) { acting_user.admin? }
+ # which is the same as saying:
+ allow_destroy(to: :all) { acting_user.admin? }
+ # you can create model specific policies in the Application Policy as well.
+ # Here we allow the author of a message to destroy the message within 5
+ # minutes of creation.
+ allow_destroy(to: Message) do
+ return true if acting_user == author && created_at > 5.minutes.ago
+ return true if acting_user.admin?
+ end
+end
+```
+
+Note that there is no `allow_read` method. Read access is granted if this browser would have the attribute broadcast to it.
+
+
#### Method Summary and Name Space Conflicts
Policy classes (and the HyperMesh::PolicyMethods module) define the following class methods:
+ `regulate_connection`
@@ -431,19 +488,5 @@
synchromesh_internal_policy_object.obj
end
...
end
```
-
-#### Setting the policy directory
-
-*HyperMesh auto-connect needs to know about all policies ahead of time so cannot rely on rails auto loading. Sorry about that!*
-
-By default HyperMesh will load all the files in the `app/policies` directory. To change the directory set the policy_directory in the synchromesh initializer.
-
-```ruby
-HyperMesh.configuration do |config|
- ...
- config.policy_directory = File.join(Rails.root, 'app', 'synchromesh-authorization')
- # can also be set to nil if you want to manually require your files
-end
-```