{"id":15588570,"url":"https://github.com/hopsoft/perm","last_synced_at":"2025-04-24T05:19:07.232Z","repository":{"id":23036262,"uuid":"26389144","full_name":"hopsoft/perm","owner":"hopsoft","description":"Simple authorization/permission management in Ruby","archived":false,"fork":false,"pushed_at":"2018-11-12T21:42:02.000Z","size":23,"stargazers_count":14,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-30T07:12:11.469Z","etag":null,"topics":["authorization","permissions","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hopsoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-11-09T07:45:17.000Z","updated_at":"2024-04-30T20:12:07.000Z","dependencies_parsed_at":"2022-08-21T18:10:46.450Z","dependency_job_id":null,"html_url":"https://github.com/hopsoft/perm","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hopsoft%2Fperm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hopsoft%2Fperm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hopsoft%2Fperm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hopsoft%2Fperm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hopsoft","download_url":"https://codeload.github.com/hopsoft/perm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250566811,"owners_count":21451311,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authorization","permissions","ruby"],"created_at":"2024-10-02T22:40:42.798Z","updated_at":"2025-04-24T05:19:07.206Z","avatar_url":"https://github.com/hopsoft.png","language":"Ruby","readme":"[![Lines of Code](http://img.shields.io/badge/lines_of_code-29-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)\n[![Maintainability](https://api.codeclimate.com/v1/badges/b4cb74544d60f1e17660/maintainability)](https://codeclimate.com/github/hopsoft/perm/maintainability)\n[![Build Status](http://img.shields.io/travis/hopsoft/perm.svg?style=flat)](https://travis-ci.org/hopsoft/perm)\n[![Coverage Status](https://img.shields.io/coveralls/hopsoft/perm.svg?style=flat)](https://coveralls.io/r/hopsoft/perm?branch=master)\n[![Downloads](http://img.shields.io/gem/dt/perm.svg?style=flat)](http://rubygems.org/gems/perm)\n\n# Perm\n\nIncredibly simple permission management i.e. authorization.\n\n## Quickstart\n\n```sh\ngem install perm\n```\n\n### Setup\n\nLet's create a simple example with __users__ \u0026 __posts__.\n\n```ruby\nclass User\n  attr_reader :roles, :posts\n\n  def initialize(roles: [])\n    @roles = roles\n    @posts = []\n  end\nend\n```\n\n```ruby\nclass Post\n  attr_reader :user, :title\n  attr_accessor :published\n\n  def initialize(user:, title:)\n    @user = user\n    @title = title\n    @user.posts \u003c\u003c self\n  end\nend\n```\n\nOnce our basic classes have be defined, we can create an authorized user to manage permissions.\n\n```ruby\nclass AuthorizedUser \u003c Perm::Authorized\n  def can_read?(post)\n    return true if user.roles.include?(:admin)\n    return true if user.roles.include?(:editor)\n    return true if user == post.user\n    post.published\n  end\n\n  def can_update?(post)\n    return true if user.roles.include?(:admin)\n    return true if user.roles.include?(:editor)\n    user == post.user\n  end\n\n  def can_delete?(post)\n    return true if user.roles.include?(:admin)\n    user == post.user\n  end\nend\n```\n\nAuthorized users do the following.\n\n- wrap user objects \u0026mdash; _somewhat like the presenter pattern_\n- add behavior to wrapped users\n- respond to authorization methods defined as `can_OPERATION?`\n- secure by default _i.e. authorization checks return false until implemented_\n\n### Usage\n\n#### Create some users\n\n```ruby\nmary = User.new(roles: [:admin])\njohn = User.new(roles: [:editor, :writer])\nbeth = User.new(roles: [:writer])\ndrew = User.new\n```\n\n#### Create a post\n\n```ruby\npost = Post.new(user: beth, title: \"Authorization made easy\")\n```\n\n#### Wrap each user with an authorizer\n```ruby\nauthorized_mary = AuthorizedUser.new(mary)\nauthorized_john = AuthorizedUser.new(john)\nauthorized_beth = AuthorizedUser.new(beth)\nauthorized_drew = AuthorizedUser.new(drew)\n\n# wrapped users continue to act like users\nauthorized_beth.posts # =\u003e [#\u003cPost:0x007fe35d081798 @title=\"Authorization made easy\"...\n\n# if conflicts arise, simply access the original\nauthorized_beth.user\n```\n\n#### Check permissions\n\n```ruby\nauthorized_mary.can_read?(post) # =\u003e true\nauthorized_mary.can_update?(post) # =\u003e true\nauthorized_mary.can_delete?(post) # =\u003e true\n\nauthorized_john.can_read?(post) # =\u003e true\nauthorized_john.can_update?(post) # =\u003e true\nauthorized_john.can_delete?(post) # =\u003e false\n\nauthorized_beth.can_read?(post) # =\u003e true\nauthorized_beth.can_update?(post) # =\u003e true\nauthorized_beth.can_delete?(post) # =\u003e true\n\nauthorized_drew.can_read?(post) # =\u003e false\nauthorized_drew.can_update?(post) # =\u003e false\nauthorized_drew.can_delete?(post) # =\u003e false\n\npost.published = true\nauthorized_drew.can_read?(post) # =\u003e true\n\n# we can also check unimplemented permissions\nauthorized_mary.can_create?(post) # =\u003e false\nauthorized_john.can_view?(post) # =\u003e false\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhopsoft%2Fperm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhopsoft%2Fperm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhopsoft%2Fperm/lists"}