{"id":19096523,"url":"https://github.com/sapcc/monsoon-openstack-auth","last_synced_at":"2026-03-09T11:33:34.967Z","repository":{"id":12800162,"uuid":"73097950","full_name":"sapcc/monsoon-openstack-auth","owner":"sapcc","description":null,"archived":false,"fork":false,"pushed_at":"2025-03-27T10:25:27.000Z","size":650,"stargazers_count":3,"open_issues_count":10,"forks_count":0,"subscribers_count":52,"default_branch":"master","last_synced_at":"2025-03-27T11:31:47.989Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sapcc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-07T16:25:16.000Z","updated_at":"2025-03-27T10:25:27.000Z","dependencies_parsed_at":"2025-03-27T11:26:51.429Z","dependency_job_id":"23b34f87-d4c9-4013-a534-fe489c5667d0","html_url":"https://github.com/sapcc/monsoon-openstack-auth","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sapcc%2Fmonsoon-openstack-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sapcc%2Fmonsoon-openstack-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sapcc%2Fmonsoon-openstack-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sapcc%2Fmonsoon-openstack-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sapcc","download_url":"https://codeload.github.com/sapcc/monsoon-openstack-auth/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249514429,"owners_count":21284542,"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":[],"created_at":"2024-11-09T03:36:57.496Z","updated_at":"2026-03-09T11:33:29.924Z","avatar_url":"https://github.com/sapcc.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"Monsoon Openstack Auth\n======================\n\nThis gem enables authentication for Ruby on Rails applications against Openstack Keystone Service using the Identity API v3. Further it implements a Policy Engine to allow permission cheks.\n\nExample\n-------\n\n```ruby\nclass DashboardController \u003c ::ScopeController\n    authentication_required domain:  :get_domain_id,\n                            project: :get_project_id,\n                            except: :terms_of_use\n\n    def index\n    end\n\n    def terms_of_use\n    end\n\n    protected\n    def get_domain_id\n        params[:domain_id]\n    end\n\n    def get_project_id\n        params[:project_id]\n    end\n```\n\nInstall\n-------\n\n```\n$ [sudo] gem install monsoon-openstack-auth\n```\n\n### Gemfile\n\n\n```\ngem 'monsoon-openstack-auth', git: 'git://github.com/sapcc/monsoon-openstack-auth.git'\n```\n\nSetup\n-----\n\n```\nrails generate monsoon_openstack_auth:setup\n```\n\nUsage\n-----\n\n### Configuration\nFile: config/initializers/monsoon_openstack_auth.rb\n\n```ruby\nMonsoonOpenstackAuth.configure do |config|\n\n  ############# Authentication ################\n  # connection driver, default MonsoonOpenstackAuth::Driver::Default (Fog)\n  # config.connection_driver = DriverClass\n\n  # api auth endpoint\n  config.connection_driver.api_endpoint = ENV['MONSOON_OPENSTACK_AUTH_API_ENDPOINT']\n\n  # optional, default=true\n  config.token_auth_allowed = true\n  # optional, default=true\n  config.basic_auth_allowed = true\n  # optional, default=true\n  config.sso_auth_allowed   = true\n  # optional, default=true\n  config.form_auth_allowed  = true\n  # optional, default=false\n  config.access_key_auth_allowed = false\n\n  # optional, default= last url before redirected to form\n  # config.login_redirect_url = '/'\n\n  ########## Authorization #########  \n  # policy_file_path: path to policy file\n  config.authorization.policy_file_path = \"config/policy.json\"\n\n  # context: Default is name of main app, e.g. dashboard.\n  # If you overwrite context, rules in policy file should begin with that context.\n  config.authorization.context = \"identity\"\n\n  # controller_action_map: default action mappings for controller actions. Can be overwritten inside controller\n  config.authorization.controller_action_map {\n       :index   =\u003e 'read',\n       :show    =\u003e 'read',\n       :new     =\u003e 'create',\n       :create  =\u003e 'create',\n       :edit    =\u003e 'update',\n       :update  =\u003e 'update',\n       :destroy =\u003e 'delete'\n  }\n  # config.authorization.security_violation_handler: Error handler method which is called when MonsoonOpenstackAuth::Authorization::SecurityViolation appears.\n  # Default setting is  :authorization_forbidden.\n  # You can specify another handler or overwrite \"authorization_forbidden\" method in controller.\n  security_violation_handler = :authorization_forbidden\n\n  # enable or disable two factor authentication\n  config.two_factor_enabled       = false\n  # two factor auth method should return a Proc with params username and passcode\n  config.two_factor_authentication_method = -\u003e username,passcode { }\n\n\n  ########## Plugin ##########\n  # optional, default=false\n  # config.debug = true\n  # optional Excon request and response debug, default=false\n  # config.debug_api_calls = true (Deprecated, use environment variable EXCON_DEBUG = true)\nend\n```\n\n### Session Store\nIf this gem should support the form based login then the session store must be anything but cookie_store.\n\nExample of setting up a [ActiveRecord session_store](https://github.com/rails/activerecord-session_store)\n\nFile: Gemfile\n\n```ruby\ngem 'activerecord-session_store'\n```\n\n```\nrails generate active_record:session_migration\n```\n\nFile: config/initializers/session_store.rb\n\n```ruby\nRails.application.config.session_store :active_record_store, :key =\u003e '_monsoon_app_session'\n```\n\n### Authentication\n\n#### ActionController::API\n\nActionController::API does not include http basic functionality. So you have to include it manually if you want to support http basic.\n\n```ruby\ninclude ActionController::HttpAuthentication::Basic::ControllerMethods\n```\n\nActionController::API does not include MonsoonOpenstackAuth::Authentication. So you have to include it manually.\n\n```ruby\ninclude MonsoonOpenstackAuth::Authentication\n```\n\n\n#### Controller\n\n##### authentication_required\n\nClass method which is called in controllers. This method is based on the before_action method and therefore it accepts the common options such as :except, :only, and :if\n\n```ruby\nauthentication_required options\n```\noptions:\n\n* **rescope**, boolean (optional). Default is true. If rescope is true the user token will be scoped to domain or/and project.\n* **domain**, symbol, value or Proc (optional?). Provides user domain id. This option is used for both the user domain and the scope domain (if rescope = false). One of options domain_id or domain_name should be provided!\n* **domain_name**, string or Proc (optional?). Use this option instead of domain_id. One of options domain_id or domain_name should be provided!\n* **project**, symbol, value or Proc (optional). Provides project id. This option is used for scope project (if rescope = true).\n* **only**, array (optional). Example only: [:index,:show]\n* **except**, optional. Example except: [:index,:show]\n* **if**, optional. Example if: -\u003e c {c.params[:region_id].nil?}\n* **unless**, optional\n\n**Example for unscoped token authentication**. User is redirected to the login page, where he/she is prompted to enter his/her credentials.\n\n```ruby\nDashboardController \u003c ApplicationController\n  authentication_required domain: 'DOMAIN_ID', rescope: false\n\n  def index\n  end\n\nend\n```\n\n**Example for project scoped token authentication**.\n```ruby\nDashboardController \u003c ApplicationController\n  authentication_required domain_name: DOMAIN_NAME, project: PROJECT_ID, rescope: true\n\n  def index\n  end\n\nend\n```\n\n**Example for only option**\n\n```ruby\nDashboardController \u003c ApplicationController\n  authentication_required only: [:index], project: :get_project, domain: :get_domain\n\n  def index\n  end\n\n  def get_domain\n    @domain_id = params[:domain_id]\n  end\n\n  def get_project\n    @project_id = params[:project_id]\n  end\nend\n```\n\n##### skip_authentication\n\nClass method which is called in controllers. This method allows you to skip authentication in subclasses.\n\n```ruby\nskip_authentication options\n```\noptions:\n\n* **only**, optional. Example only: [:index,:show]\n* **except**, optional. Example except: [:index,:show]\n* **if**, optional. Example if: -\u003e c {!c.params[:domain].nil?}\n* **unless**, optional\n\n##### Prevent rescoping\n\nIt is possible to prevent automatic rescoping.\n\n```ruby\nDashboardController \u003c ApplicationController\n  authentication_required domain_name: -\u003e c {c.params[:domain_id]}, project: project_id, rescope: false\n  before_action do\n    # user is authenticated with unscoped token. Check if user has read permission for project_id\n    user_projects = service_user.user_projects(current_user.id)\n    redirect_to not_allowed_url unless user_projects.collect{|project| project.id}.include?(project_id)\n  end\n\n  before_action do\n    authentication_rescope_token\n    # now current_user is rescoped to project_id\n  end\n\n  def index\n  end\n\n  protected\n  def project_id\n    @project_id ||= params[:project_id]\n  end\nend\n```\n\n##### current_user\n\nInstance method, available in controller instances and views. Returns current logged in user or nil. To get current_user the authentication_required method should be called first.\n\n##### logged_in?\n\nInstance method, available in controller instances and views. Returns true if current logged in user is\npresented.\n\n#### User Class (current_user)\n\nInstance methods:\n\n* **context**, returns the token received by API\n* **enabled?**, true if user is active (enabled)\n* **token**, returns the token value (auth_token)  \n* **id**, user id (obtained through the token)\n* **name**, user name (obtained through the token)\n* **description**, user description (obtained through the token)\n* **user_domain_id**, received by scoped token\n* **user_domain_name**, received by scoped token\n* **domain_id**, scope (obtained through the token)\n* **domain_name**, scope (obtained through the token)\n* **project_id**, scope (obtained through the token)\n* **project_name**, scope (obtained through the token)\n* **project_domain_id**, scope (obtained through the token)\n* **project_domain_name**, scope (obtained through the token)\n* **project_scoped**, returns a hash (scope)\n* **domain_scoped**, returns a hash (scope)\n* **token_expires_at**, returns datetime\n* **token_expired?**, true if token expired\n* **token_issued_at**, returns datetime\n* **service_catalog**, returns an array of hashes (services)\n* **has_service?(type)**, returns true if service_catalog contains the given type\n* **roles**, returns an array of hashes\n* **role_names**, returns an array of roles\n* **has_role?(name)**, returns true if user has the given role\n* **admin?**, true if user is a superuser (can do anything)\n* **default_services_region**, returns the first endpoint region for first non-identity service in the service catalog\n* **available_services_regions**, returns list of unique region name values found in service catalog\n* **is_allowed?(RULE_NAMES)**, returns true or false\n* **required_roles(RULE_NAMES)**, returns an array of required roles\n\n\n### Authorization\n\nAuthorization is inspired by the [authority gem from nathanl](https://github.com/nathanl/authority). In contrast to it's origin, authorization uses policy files for\nthe authorization checks and not authorizer classes.\nSo you now have to implement a policy file in json format for you application. The file has to be located under `config.authorization.policy_file_path` .\n\nAn example could look like:\n\n```json\n{\n  \"default\": \"rule:admin_required\",\n  \"p_member\" : \"project_id:%(project.id)s\",\n  \"d_member\" : \"domain_id:%(domain.id)s\",\n  \"admin_required\": \"role:admin or is_admin:True\",\n  \"admin_or_project_member\": \"rule:admin_required or rule:p_member\",\n  \"admin_or_domain_member\": \"rule:admin_required or rule:d_member\",  \n\n  \"identity:domain_list\":    \"rule:test or rule:admin_required or rule:is_service or rule:admin_or_domain_member\",\n  \"identity:domain_show\":     \"rule:admin_required or rule:d_member\",\n  \"identity:domain_create\":   \"\",\n  \"identity:domain_change\":   \"rule:admin_required or rule:d_member\",\n  \"identity:domain_delete\":   \"rule:admin_required\",\n  \"identity:project_list\":    \"rule:admin_or_domain_member\",\n  \"identity:project_create\":  \"rule:admin_or_domain_member\",\n  \"identity:project_change\":  \"rule:admin_or_domain_member or rule:p_member\"\n}\n```\n\nThe policy syntax is described at [openstack olso policies](http://docs.openstack.org/developer/oslo.policy/api.html).\n\nSome explanations on that:\n\n```\"default\": \"rule:admin_required\" ``` defines the default rule which is used in case that an authorization request with an undefined rule is made.\n\n```\"admin_required\": \"role:admin or is_admin:True\"``` defines a rule which can later be referenced in other rules. It uses the role and is_admin attributes from the current user for whom a authorization request is made.\n\n```\"d_member\" : \"domain_id:%(domain.id)s\"``` when the user domain_id from his current authentication scope is the same as the one given to the auth. request. The auth. request could be made with an Domain object which has an id attribute.\n\n```\"identity:project_list\":    \"rule:admin_or_domain_member\"``` defines a application specific rule where the context is identity (might come from config.authorization.context) and the action to be checked is project_list.\n\n#### Explizit authorization enforcement\n\nYou've to use the policy_engine to do a policy enforcement. The engine is always available\n\n```policy_engine = MonsoonOpenstackAuth.policy_engine```\n\nAfterwards you can do a policy check for a user with\n\n```ruby\naction = \"identity:project_list\"\npolicy_engine.policy(@current_user).enforce(action)\n```\nYou get a `true or false` as an result.\n\n#### User authorization checks\n\nSimilar to the above but more convinient you can check authorizations for a user with the `is_allowed?` method. So you can ask\n\n```ruby\naction = \"identity:project_list\"\n@current_user.is_allowed?(action, @domain)\n```\nand get a boolean response.\n\n#### Controller authorization enforcements\n\nControllers get some additional class methods for authorization purpose automatically through a railtie.\n\nYou can check authorization in your controllers in one of two ways:\n\n`authorization_actions_for ModelClass [, :name =\u003e 'ModelNameUsedInPolicy', :actions =\u003e {:action_name =\u003e 'policy_action_name'}, \u003cStandardBeforeFilterOptions\u003e ]`\n\nprotects multiple controller actions with a before_action, which performs a class-level check. If the current user is never allowed to delete a ModelClass, he'll never even get to the controller's destroy method.\n\n`authorization_action_for @model [, :name =\u003e 'ModelNameUsedInPolicy' ]`\n\ncan be called inside a single controller action, and performs an instance-level check. If called inside update, it will check whether the current user is allowed to update this particular @model instance.\n\nIf either method finds a user attempting something they're not authorized to do, a Security Violation will result.\n\nHow does `authorization_actions_for` know to check deletable_by? before the controller's destroy action? It checks your configuration from config.authorization.controller_action_map configured in the initializer file.\n\nThe mappings are also configurable per controller with\n\n```ruby\nauthorization_actions :index =\u003e 'list', :update =\u003e 'change'\n```\n\nAlternatively you can call an authorization check by it's rule directly with an\n\n```ruby\n  if_allowed?(PolicyFileRule [, {key: value, ...}])\n```\n\n#### User authorization checks\n\nAuthorizations for a user can be checked by the `is_allowed?` method. So you can ask\n\n```ruby\n@current_user.is_allowed?(PolicyFileRule, params)\n```\n\nExample:\n```ruby\n  @current_user.is_allowed?(\"identity:project_create\", {domain_id: 1})\n  @current_user.is_allowed?([\"identity:project_create\",\"identity:project_change\"], {domain_id: 1})\n```\n\n\nDevelop\n-------\n```\ngit clone https://github.com/sapcc/monsoon-openstack-auth.git\ncd monsoon-openstack-auth\nbundle install\ncd spec/dummy\nbundle exec rake db:migrate\nbundle exec rails s\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsapcc%2Fmonsoon-openstack-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsapcc%2Fmonsoon-openstack-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsapcc%2Fmonsoon-openstack-auth/lists"}