# Authorisation & Scopes

**URL:** <https://discourse.rom-rb.org/t/authorisation-scopes/464>\
**Category:** Architecture Design\
**Created:** [May 12, 2021, 4:01pm UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464 "2021-05-12T16:01:45Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![DangerDawson](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/dangerdawson/32/237_2.png) [@DangerDawson](https://discourse.rom-rb.org/u/DangerDawson)\
**Post date:** [May 12, 2021, 4:01pm UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/1 "2021-05-12T16:01:45Z")

</div>

I am going to implement authorisation within a Hanami project and I am looking how best to implement scoping with the Repository / Entity model, and instead of re-inventing the wheel I was hoping someone else had done so successfully. e.g. not passing the relation to the authorisation layer and chaining expressions `where(admin: true)`

My current thinking is to get the authorisation layer return a Proc, that can be passed into the repository method as a scope argument

e.g.

```auto
scope = proc { |r| r.where(admin: true) }

user_repo.some_method(scope: scope)

```

thoughts?

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/solnic/32/279_2.png) [@solnic](https://discourse.rom-rb.org/u/solnic)\
**Post date:** [May 15, 2021, 8:56am UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/2 "2021-05-15T08:56:30Z")

</div>

This will be a plugin in rom 6.0 but for now it’s just a matter of overriding `root` method and using `super`, ie:

```ruby
def root
  super.where(admin: true)
end

```

As you can probably imagine, this can be encapsulated by some macro, like `scope :admin` that would define `root` automatically. It’s also nice to have such scopes defined as relation views for better encapsulation. This can feel like an overkill but only in the beginning 🙂

---

<div class="post-metadata">

**Author:** ![apohllo](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/apohllo/32/86_2.png) [@apohllo](https://discourse.rom-rb.org/u/apohllo)\
**Post date:** [May 17, 2021, 8:56am UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/3 "2021-05-17T08:56:36Z")

</div>

@solnic for clarification: the idea is to have separate repos for different levels of authorization? Since overriding root is only possible on the repo level I suppose.

---

<div class="post-metadata">

**Author:** ![DangerDawson](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/dangerdawson/32/237_2.png) [@DangerDawson](https://discourse.rom-rb.org/u/DangerDawson)\
**Post date:** [May 17, 2021, 9:25am UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/4 "2021-05-17T09:25:27Z")

</div>

@solnic Thank you for your reply.

I am currently injecting the repositories as a dependency in my controllers e.g.

`include Import[entity_repo: 'repositories.entities.entity']`

So I suppose the only way to achieve this would to either take the dependency out of the import, and as @apohllo suggests have a separate instance per level of authorisation, although this does pose a problem when the scope has a dynamic part to it e.g. `user_id` as we would have to pass this in as part of the constructor, and I would also lose all the benefit of reduced object allocation by freezing and memoizing the repository dependency which is what I am currently doing at the moment.

---

<div class="post-metadata">

**Author:** ![apohllo](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/apohllo/32/86_2.png) [@apohllo](https://discourse.rom-rb.org/u/apohllo)\
**Post date:** [May 19, 2021, 11:18am UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/5 "2021-05-19T11:18:49Z")

</div>

Well, generally I think that the reason for introducing repositories is the fact that you don’t want any details of the data layer leaking into the controllers. Passing the proc from controller to the repo, I believe, is a direct violation of this rule. There are the following options I suppose:

- have different repos, for different levels of authorization, e.g. admin API would use AdminRepos (this works well with memoization - you just need to define an AdminRepo which inherits from the “default” repo and changes the root as @solnic suggests; yet it doesn’t work with the user\_id)
- make the user id/authorization level/whatever a part of the API of the repository - similar to your solution, but the difference here is that you don’t pass any db-layer details from the authorization layer. E.g. instead of passing the scope, you pass user\_id or user role or some other type of requirement. How it is interpreted is essentially the responsibility of the db-layer
- use [dry-effect/reader](https://dry-rb.org/gems/dry-effects/0.1/effects/reader/) - say `with_scope` which would be consumed by the repo. You can pass the scope, but also you can pass more model-oriented data, like the user\_id, user roles etc.

---

<div class="post-metadata">

**Author:** ![apohllo](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/apohllo/32/86_2.png) [@apohllo](https://discourse.rom-rb.org/u/apohllo)\
**Post date:** [May 19, 2021, 11:53am UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/6 "2021-05-19T11:53:34Z")

</div>

To elaborate a bit on the “AdminRepo” - I mean you would need an AdminRepo class, eg. `AdminNewsRepo` or `Admin::NewsRepo` for each regular repo, eg. NewsRepo. This plays well only if you have two levels of authorization, it does not play well with the user\_id.

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/solnic/32/279_2.png) [@solnic](https://discourse.rom-rb.org/u/solnic)\
**Post date:** [May 20, 2021, 7:28am UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/7 "2021-05-20T07:28:38Z")

</div>

Using multiple repo classes for the same struct type for this purposes is indeed a solution. I did exactly this and typically I’d have ie `Public::StuffRepo` and `Admin::StuffRepo` with admin-specific logic.

If there’s some “context” from the outside that’s needed to perform a query, then I 100% encourage you to try out dry-effects like @apohllo suggested. It’s very easy to add it to an existing code base and it simplifies many things.

Essentially, I just repeated what Aleksander said 😃

---

<div class="post-metadata">

**Author:** ![DangerDawson](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/dangerdawson/32/237_2.png) [@DangerDawson](https://discourse.rom-rb.org/u/DangerDawson)\
**Post date:** [May 20, 2021, 8:09am UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/8 "2021-05-20T08:09:24Z")

</div>

I actually started down the route of dry-effect/reader, although there is a gem dependency which prevents it being used with Hanami which if memory serves me correctly was [https://dry-rb.org/gems/dry-initializer/3.0/](https://dry-rb.org/gems/dry-initializer/3.0/). I may try forking the repo and see if I can downgrade the dependency to use the same version I am currently locked to.

@solnic / @apohllo thank you for your thoughts, I am going hopefully work on this next week and I will report back with what I decide.

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/solnic/32/279_2.png) [@solnic](https://discourse.rom-rb.org/u/solnic)\
**Post date:** [May 21, 2021, 7:46am UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/9 "2021-05-21T07:46:49Z")

</div>

@DangerDawson lemme know if you need help with dependencies/versions. We may need to adjust some gemspecs maybe, so don’t hesitate to report issues.

---

<div class="post-metadata">

**Author:** ![DangerDawson](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/dangerdawson/32/237_2.png) [@DangerDawson](https://discourse.rom-rb.org/u/DangerDawson)\
**Post date:** [May 21, 2021, 1:56pm UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/10 "2021-05-21T13:56:59Z")

</div>

@solnic I have managed to get dry-effects working with hanami by doing the following:

Bumping the dependency in the rom 3.3.3. gemspec to:

`gem.add_runtime_dependency "dry-initializer", "~> 3.0"`

disabling the following check: [dry-initializer/build\_nested\_type.rb at master · dry-rb/dry-initializer · GitHub](https://github.com/dry-rb/dry-initializer/blob/master/lib/dry/initializer/dispatchers/build_nested_type.rb#L36)

```auto
 return unless name[/^_|__|_$/]

```

Which was being thrown by the following in rom-repository: `https://github.com/rom-rb/rom-repository/blob/v1.4.0/lib/rom/repository/changeset/stateful.rb#L16`  
(I have a limit of 2 links per post, hence the disabled hyperlink)

```auto
option : __data__ , optional: true

```

Updating the following: [rom/initializer.rb at v3.3.0 · rom-rb/rom · GitHub](https://github.com/rom-rb/rom/blob/v3.3.0/lib/rom/initializer.rb#L22)

from:

```auto
 def options
    @ __options__
 end

```

to

```auto
 def options
  self.class.dry_initializer.attributes(self)
 end

```

Which is a big hack, any maybe not correct, but it got it working.

What are your thoughts on the best way to implement this? shall I just fork `rom` and update the gemspec, then monkey patch it within my hanami project?

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/solnic/32/279_2.png) [@solnic](https://discourse.rom-rb.org/u/solnic)\
**Post date:** [May 24, 2021, 7:32am UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/12 "2021-05-24T07:32:45Z")

</div>

Oh thanks for digging into this!

@flash-gordon have we missed some improvement in dry-initializer and forgot to update `ROM::Initializer` accordingly? Seems like we have 🙂Maybe we should fix it and bump dry-initializer in current 5.x line?

---

<div class="post-metadata">

**Author:** ![DangerDawson](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/dangerdawson/32/237_2.png) [@DangerDawson](https://discourse.rom-rb.org/u/DangerDawson)\
**Post date:** [May 24, 2021, 8:19am UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/13 "2021-05-24T08:19:01Z")

</div>

@solnic I believe it would be a case of doing a search and replace for ` __data__ ` which is not allowed as a option in the latest version of `dry-initializer` e.g. I renamed it to `private_data`

BTW. this breaking change has just arrived in the latest version of `dry-initializer`

> <https://github.com/dry-rb/dry-initializer/blob/master/CHANGELOG.md#301-2019-04-15>

Also the following can be simplified:

> <https://github.com/rom-rb/rom/blob/9204ff45fa39095e4f23158021a6951225197451/lib/rom/initializer.rb#L63>

with

```auto
self.class.dry_initializer.attributes(self)

```

?

---

<div class="post-metadata">

**Author:** ![flash-gordon](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/flash-gordon/32/186_2.png) [@flash-gordon](https://discourse.rom-rb.org/u/flash-gordon)\
**Post date:** [May 24, 2021, 2:44pm UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/14 "2021-05-24T14:44:48Z")

</div>

> [@solnic](#):
>
> @flash-gordon have we missed some improvement in dry-initializer and forgot to update `ROM::Initializer` accordingly? Seems like we have 🙂Maybe we should fix it and bump dry-initializer in current 5.x line?

I don’t have time to look in details but it wouldn’t work, we updated code with lazy assignment in `InstanceMethods`.

```ruby
self.class.dry_initializer.attributes(self)

```

It would be slower than current version since it doesn’t cache.

---

<div class="post-metadata">

**Author:** ![DangerDawson](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/dangerdawson/32/237_2.png) [@DangerDawson](https://discourse.rom-rb.org/u/DangerDawson)\
**Post date:** [May 24, 2021, 7:22pm UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/15 "2021-05-24T19:22:01Z")

</div>

Sorry I meant:

```auto
@ __options__ ||= self.class.dry_initializer.attributes(self)

```

Although if:

```auto
@ __options__ ||= self.class.dry_initializer.definitions.values.each_with_object({}) do |item, obj|
  obj[item.target] = instance_variable_get(item.ivar)
end

```

Gives a different behaviour I will fork rom v3.3.0 and use that instead of what I proposed

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.rom-rb.org/solnic/32/279_2.png) [@solnic](https://discourse.rom-rb.org/u/solnic)\
**Post date:** [May 25, 2021, 7:41am UTC](https://discourse.rom-rb.org/t/authorisation-scopes/464/16 "2021-05-25T07:41:50Z")

</div>

> [@DangerDawson](#):
>
> Gives a different behaviour I will fork rom v3.3.0 and use that instead of what I proposed

oh boy I totally missed the fact that we’re talking about rom 3.3 😕
