<commit_before># ActivityPermissionEngine

This gem provide flexible tooling for managing application permissions

It allow you to :

* Set permissions on activities (strings) for some entities like roles.
* Check for authorization

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'activity_permission_engine'
```

And then execute:

    $ bundle

Or install it yourself as:

    $ gem install activity_permission_engine

## Usage

### Configure

```ruby
ActivityPermissionEngine.configure do |config|
  config.activity_permissions_registry =  # Provide the persistence adapter from lib/adapters/activity_permissions/registry
  config.activities = ['accounting:payments:register','accounting:accounts:read'] # The list of activities, can be provided at run time.
end
```

## Contributing

1. Fork it ( https://github.com/synbioz/activity_permission_engine/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)<commit_msg>doc(readme): add basic usages documentation<commit_after># ActivityPermissionEngine

This gem provide flexible tooling for managing application permissions

It allow you to :

* Set permissions on activities (strings) for some entities like roles.
* Check for authorization

You can use it on it's own but, it will fit very well with [Pundit](https://github.com/elabs/pundit) or cancan

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'activity_permission_engine'
```

And then execute:

    $ bundle

Or install it yourself as:

    $ gem install activity_permission_engine

## Usage

### Configure

You need a persistence adapter. See [activity_permission_engine_redis](https://github.com/synbioz/activity_permission_engine_redis)

```ruby
ActivityPermissionEngine.configure do |config|
  config.activity_permissions_registry =  # Provide the persistence adapter choose from existing one around the web or create yours
  config.activities = ['accounting:payments:register','accounting:accounts:read'] # Optional. The list of activities, can be provided at run time.
end
```

### Roles refs and activities refs

This library does not make assumptions upon roles except that they should be strings.
But we recommend to use business role from the organization chart.

eg : 'accounting:payment:register' may be allowed to 'accountant' and/or 'sales_executive'
'person:update_profile' maybe to 'it_staff:administrator' 

But keep in mind to only use references ( not database id). References do not changes and are easy to translate in user readable value.
( with I18n if you wish )


### Manage activities

Activities are part of code and rely on code, they can not persist they are refreshed at each application start.

eg:  'accounting:payment:register' or 'person:update_profile'

Activities can be provided at configuration time or run time. Up to you to choose / mix strategies.

At run time you may want to create helper method that register the activity once the file is required.
At configuration time you'll likely maintain a file that contains all the activity refs.


#### Register an activity

`ActivityPermissionEngine.register_activity(#activity_ref)`

Given an object that respond to `#activity_ref` (You can use ActivityPermissionEngine::RegisterActivity::Request)
It will add the activity_ref ( basically a string ) within the activities store.

```ruby
ActivityPermissionEngine.register_activity(ActivityPermissionEngine::RegisterActivity::Request.new('accounting:payments:register'))
#=> #<ActivityPermissionEngine::RegisterActivity::Response:0x0055c7cdc90f00 @success=true>
```

#### List activities

`ActivityPermissionEngine.list_activities`

Returns the list of activities. You'll need this to provide in your UI the list of activities.
The user should then allow role to perform activities.

```ruby
ActivityPermissionEngine.list_activities
#=> #<ActivityPermissionEngine::ListActivities::Response:0x0055c7cdc78d38 @activity_refs=["accounting:payments:register"]>
```


### Activity permissions

Activities permissions are persisted ( if you set the correct adapter ).
This data structure hold the role_ref allowed to perform an activity_ref

#### Allow role to perform activity

To allow a role to perform an activity use 

`ActivityPermissionEngine.allow_activity(#activity_ref, #role_ref)`

Given an object that respond to `#activity_ref` and `#role_ref` (You can use ActivityPermissionEngine::AllowActivity::Request)

```ruby
ActivityPermissionEngine.allow_activity(ActivityPermissionEngine::AllowActivity::Request.new('accounting:payments:register', 'accountant'))
#=> #<ActivityPermissionEngine::AllowActivity::Response:0x0055c7cdc63938 @success=["accountant"]>

ActivityPermissionEngine.allow_activity(ActivityPermissionEngine::AllowActivity::Request.new('accounting:payments:register', 'sales:executives'))
#=> #<ActivityPermissionEngine::AllowActivity::Response:0x0055c7cdc42828 @success=["accountant", "sales:executives"]>
```

#### Disallow role to perform activity

To disallow a role to perform an activity use 

`ActivityPermissionEngine.disallow_activity(#activity_ref, #role_ref)`

Given an object that respond to `#activity_ref` and `#role_ref` (You can use ActivityPermissionEngine::AllowActivity::Request)

```ruby
ActivityPermissionEngine.disallow_activity(ActivityPermissionEngine::AllowActivity::Request.new('accounting:payments:register', 'sales:executives'))
#=> #<ActivityPermissionEngine::DisallowActivity::Response:0x0055c7cdc10c88 @success=["accountant"]
```

#### List existing permissions

A permission exists once you allow a user to perform an activity.

Use 

`ActivityPermissionEngine.list_activities_permissions` to get a full list of existing activity permissions.

It returns a Response object that respond to `#activities_permissions` and returns an Array of activities permissions. 

```ruby
request = ActivityPermissionEngine.list_activities_permissions
#=> #<ActivityPermissionEngine::ListActivitiesPermissions::Response:0x0055c7cdbe4278 @activities_permissions=[#<ActivityPermissionEngine::ActivityPermissionsRegistry::ActivityPermission:0x0055c7cdbe42a0 @activity_ref="accounting:payments:register", @role_refs=["accountant"]>]>
request.activities_permissions
#=> [#<ActivityPermissionEngine::ActivityPermissionsRegistry::ActivityPermission:0x0055c7cdbe42a0 @activity_ref="accounting:payments:register", @role_refs=["accountant"]>]
```

### Authorization check

To check for authorization you will use `ActivityPermissionEngine.check_authorization(#activity_ref, #role_refs)`
The request must respond to role_refs with an array, since most of the time a user can have many roles.
It only need a single matching one to be authorized.

```ruby
response = ActivityPermissionEngine.check_authorization(ActivityPermissionEngine::CheckAuthorization::Request.new('accounting:payments:register',['accountant']))
#=> #<ActivityPermissionEngine::CheckAuthorization::Response:0x0055c7cdb95600 @authorized=true>
response.authorized?
#=> true
```

### Request objects

You are free to not use the request objects like `ActivityPermissionEngine::AllowActivity::Request`
You just need to supply an object that respond to the same methods like a struct.

But I think it could be wise to use then in order to ensure API compliance and forward compatibility.
By the way they are readonly data structure.

## Contributing

1. Fork it ( https://github.com/synbioz/activity_permission_engine/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
{'fraction_non_alphanumeric': 0.08777223825767515, 'fraction_numerical': 0.012988716872212017, 'mean_word_length': 6.802456499488229, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 20, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5336612', 'n_tokens_mistral': 2106, 'n_tokens_neox': 1979, 'n_words': 703}