{"id":19316721,"url":"https://github.com/maxim/authoraise","last_synced_at":"2025-04-22T17:30:25.531Z","repository":{"id":28421075,"uuid":"31935741","full_name":"maxim/authoraise","owner":"maxim","description":"Authorize without false negatives","archived":false,"fork":false,"pushed_at":"2023-01-08T19:20:16.000Z","size":21,"stargazers_count":18,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-17T08:29:10.464Z","etag":null,"topics":["authorization","minimalist","ruby"],"latest_commit_sha":null,"homepage":null,"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/maxim.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-10T03:11:18.000Z","updated_at":"2023-06-19T00:36:44.000Z","dependencies_parsed_at":"2023-01-14T08:47:34.682Z","dependency_job_id":null,"html_url":"https://github.com/maxim/authoraise","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxim%2Fauthoraise","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxim%2Fauthoraise/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxim%2Fauthoraise/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxim%2Fauthoraise/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maxim","download_url":"https://codeload.github.com/maxim/authoraise/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250287337,"owners_count":21405588,"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","minimalist","ruby"],"created_at":"2024-11-10T01:12:23.687Z","updated_at":"2025-04-22T17:30:25.250Z","avatar_url":"https://github.com/maxim.png","language":"Ruby","readme":"# Authoraise\n\n[![Code Climate](https://codeclimate.com/github/maxim/authoraise/badges/gpa.svg)](https://codeclimate.com/github/maxim/authoraise)\n\nThis gem is not like other authorization gems because it doesn't enforce any kind of structure or vocabulary on your app. Its only job is to wrap and audit your boolean expressions that you use for authorization.\n\nSo instead of writing boolean expressions like this.\n\n~~~ruby\n  options[:post] \u0026\u0026\n    (options[:post].published? || (options[:post].user == options[:user]))\n~~~\n\nYou would write them like this\n\n~~~ruby\n  policy = Authoraise::Policy.new\n  policy.allow { |post| post.published? }\n  policy.allow { |post, user| post.user == user }\n  policy.authorize(options)\n~~~\n\nOr like this (my personal favorite)\n\n~~~ruby\n  policy = Authoraise::Policy.new do |p|\n    p.allow { |post| post.published? }\n    p.allow { |post, user| post.user == user }\n  end\n  policy.authorize(options)\n~~~\n\nOr like this, where declaration and authorization both happen inline\n\n~~~ruby\n  authorize(options) do |policy|\n    policy.allow { |post| post.published? }\n    policy.allow { |post, user| post.user == user }\n  end\n~~~\n\nYou may wonder why would you do that. Well, when your authorization logic gets more complex, you might start forgetting to pass in all the options that are used to check access. When that happens, your boolean expressions return false, causing false negatives. Take a look at the first example above, and think what happens if post is not published and `options[:user]` is not passed in. Hint: you just get a `false`. Your program would lie to you, because really you never gave it a user to check, so how does it know if it's a false? It's straight up missing some data.\n\nThis gem solves the problem by raising helpful error messages, but also allowing you to ignore the issue where it's intended to be that way. So in the examples above if you pass an unpublished post and forget to pass in a user in the options, you will see a helpful error message.\n\n## Usage\n\nFollow these examples to understand how things work in various cases.\n\n~~~ruby\nrequire 'authoraise'\n\n# Authorization policy can be defined as follows...\npolicy = Authoraise::Policy.new\npolicy.allow { |user| user == 'sammy' }\npolicy.allow { |post| post == 'happy_post' }\n\n# ...and used as follows.\npolicy.authorize(user: 'sammy', post: 'happy_post') # =\u003e true\npolicy.authorize(user: 'bob',   post: 'happy_post') # =\u003e true\npolicy.authorize(user: 'bob',   post: 'sad_post')   # =\u003e false\npolicy.authorize(user: 'sammy')                     # =\u003e true\n\n# Another way is to both define and run a policy using this block form.\ninclude Authoraise\nauthorize(user: 'sammy', post: 'article') do |policy|\n  policy.allow { |user| user == 'sammy' }\nend\n# =\u003e true\n\n# Oops, in this example I forgot to pass the post, but user also didn't match.\nauthorize(user: 'bob') do |policy|\n  policy.allow { |user| user == 'sammy' }\n  policy.allow { |post| post == 'foo' }\nend\n# =\u003e Authoraise::Error: Inconclusive authorization, missing keys: [:post]\n\n# Forgot to pass the post object, but user was actually enough.\nauthorize(user: 'sammy') do |policy|\n  policy.allow { |user| user == 'sammy' }\n  policy.allow { |post| post == 'foo' }\nend\n# =\u003e true\n\n# Didn't forget to pass anything, but it didn't match, so this is a legit fail.\nauthorize(user: 'bob', post: 'foo') do |policy|\n  policy.allow { |user| user == 'sammy' }\n  policy.allow { |post| post == 'bar' }\nend\n# =\u003e false\n\n# Let's see what happens in strict mode.\nAuthoraise.strict_mode = true\n\n# In strict mode any missing key raises an error, even if other checks passed.\nauthorize(user: 'sammy') do |policy|\n  policy.allow { |user| user == 'sammy' }\n  policy.allow { |post| post == 'foo' }\nend\n# =\u003e Authoraise::Error: Strict mode found missing keys: [:post]\n~~~\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'authoraise'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install authoraise\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\n1. Fork it ( https://github.com/maxim/authoraise/fork )\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxim%2Fauthoraise","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxim%2Fauthoraise","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxim%2Fauthoraise/lists"}