rails_keycloak_authorization 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +139 -0
- data/Rakefile +8 -0
- data/app/assets/config/rails_keycloak_authorization_manifest.js +1 -0
- data/app/assets/stylesheets/rails_keycloak_authorization/application.css +15 -0
- data/app/controllers/concerns/rails_keycloak_authorization/with_htmx_layout.rb +15 -0
- data/app/controllers/concerns/rails_keycloak_authorization/with_keycloak_admin.rb +35 -0
- data/app/controllers/concerns/rails_keycloak_authorization/with_routes_reader.rb +26 -0
- data/app/controllers/rails_keycloak_authorization/application_controller.rb +4 -0
- data/app/controllers/rails_keycloak_authorization/management_controller.rb +9 -0
- data/app/controllers/rails_keycloak_authorization/permissions_controller.rb +39 -0
- data/app/controllers/rails_keycloak_authorization/policies_controller.rb +19 -0
- data/app/controllers/rails_keycloak_authorization/resources_controller.rb +31 -0
- data/app/controllers/rails_keycloak_authorization/routes_controller.rb +23 -0
- data/app/controllers/rails_keycloak_authorization/scopes_controller.rb +48 -0
- data/app/helpers/rails_keycloak_authorization/application_helper.rb +4 -0
- data/app/helpers/rails_keycloak_authorization/resources_helper.rb +5 -0
- data/app/jobs/rails_keycloak_authorization/application_job.rb +4 -0
- data/app/mailers/rails_keycloak_authorization/application_mailer.rb +6 -0
- data/app/models/rails_keycloak_authorization/application_record.rb +5 -0
- data/app/services/rails_keycloak_authorization/keycloak_admin_ruby_agent.rb +145 -0
- data/app/views/layouts/rails_keycloak_authorization/application.html.erb +14 -0
- data/app/views/layouts/rails_keycloak_authorization/htmx.html.erb +14 -0
- data/app/views/rails_keycloak_authorization/management/index.html.erb +79 -0
- data/app/views/rails_keycloak_authorization/permissions/index.html.erb +41 -0
- data/app/views/rails_keycloak_authorization/permissions/resource_scopes_select.html.erb +7 -0
- data/app/views/rails_keycloak_authorization/policies/index.html.erb +23 -0
- data/app/views/rails_keycloak_authorization/resources/index.html.erb +5 -0
- data/app/views/rails_keycloak_authorization/resources/new.html.erb +4 -0
- data/app/views/rails_keycloak_authorization/resources/show.html.erb +28 -0
- data/app/views/rails_keycloak_authorization/routes/index.html.erb +58 -0
- data/app/views/rails_keycloak_authorization/routes/show.html.erb +6 -0
- data/app/views/rails_keycloak_authorization/scopes/index.html.erb +9 -0
- data/app/views/rails_keycloak_authorization/scopes/show.html.erb +33 -0
- data/config/routes.rb +13 -0
- data/lib/rails_keycloak_authorization/engine.rb +8 -0
- data/lib/rails_keycloak_authorization/version.rb +3 -0
- data/lib/rails_keycloak_authorization.rb +77 -0
- data/lib/tasks/rails_keycloak_authorization_tasks.rake +170 -0
- metadata +128 -0
| @@ -0,0 +1,79 @@ | |
| 1 | 
            +
            <h1 class="text-lg font-medium text-center text-gray-500">Rails Keycloak Authorization Gem</h1>
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            <div class="text-sm font-medium text-center text-gray-500 border-b border-gray-200 dark:text-gray-400 dark:border-gray-700" role="tablist">
         | 
| 4 | 
            +
              <ul class="flex flex-wrap -mb-px">
         | 
| 5 | 
            +
                <li>
         | 
| 6 | 
            +
                  <button
         | 
| 7 | 
            +
                    id="routes-tab"
         | 
| 8 | 
            +
                    hx-get="<%= routes_path %>"
         | 
| 9 | 
            +
                    class="inline-block p-4 rounded-t-lg border-b-2 text-blue-600 border-blue-600 dark:text-blue-500 dark:border-blue-500"
         | 
| 10 | 
            +
                    hx-on::after-request="
         | 
| 11 | 
            +
                      document.querySelector('#routes-tab').className = 'inline-block p-4 rounded-t-lg border-b-2 text-blue-600 border-blue-600 dark:text-blue-500 dark:border-blue-500';
         | 
| 12 | 
            +
                      document.querySelector('#policies-tab').className = 'inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500';
         | 
| 13 | 
            +
                      document.querySelector('#permissions-tab').className = 'inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500';
         | 
| 14 | 
            +
                      document.querySelector('#resources-tab').className ='inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500';
         | 
| 15 | 
            +
                    "
         | 
| 16 | 
            +
                    hx-target="#tab-content"
         | 
| 17 | 
            +
                    hx-swap="innerHTML"
         | 
| 18 | 
            +
                    role="tab"
         | 
| 19 | 
            +
                    aria-selected="true"
         | 
| 20 | 
            +
                    aria-controls="tab-content">Rails Routes</button>
         | 
| 21 | 
            +
                </li>
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                <li>
         | 
| 24 | 
            +
                  <button
         | 
| 25 | 
            +
                    id="policies-tab"
         | 
| 26 | 
            +
                    hx-get="<%= policies_path %>"
         | 
| 27 | 
            +
                    class="inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500"
         | 
| 28 | 
            +
                    hx-on::after-request="
         | 
| 29 | 
            +
                      document.querySelector('#permissions-tab').className ='inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500';
         | 
| 30 | 
            +
                      document.querySelector('#routes-tab').className ='inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500';
         | 
| 31 | 
            +
                      document.querySelector('#policies-tab').className = 'inline-block p-4 rounded-t-lg border-b-2 text-blue-600 border-blue-600 dark:text-blue-500 dark:border-blue-500';
         | 
| 32 | 
            +
                      document.querySelector('#resources-tab').className ='inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500';
         | 
| 33 | 
            +
                    "
         | 
| 34 | 
            +
                    hx-target="#tab-content"
         | 
| 35 | 
            +
                    hx-swap="innerHTML"
         | 
| 36 | 
            +
                    role="tab"
         | 
| 37 | 
            +
                    aria-selected="true"
         | 
| 38 | 
            +
                    aria-controls="tab-content">Keycloak Policies</button>
         | 
| 39 | 
            +
                </li>
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                <li>
         | 
| 42 | 
            +
                  <button
         | 
| 43 | 
            +
                    id="permissions-tab"
         | 
| 44 | 
            +
                    hx-get="<%= permissions_path %>"
         | 
| 45 | 
            +
                    class="inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500"
         | 
| 46 | 
            +
                    hx-on::after-request="
         | 
| 47 | 
            +
                      document.querySelector('#routes-tab').className ='inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500';
         | 
| 48 | 
            +
                      document.querySelector('#policies-tab').className ='inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500';
         | 
| 49 | 
            +
                      document.querySelector('#permissions-tab').className = 'inline-block p-4 rounded-t-lg border-b-2 text-blue-600 border-blue-600 dark:text-blue-500 dark:border-blue-500';
         | 
| 50 | 
            +
                      document.querySelector('#resources-tab').className ='inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500';
         | 
| 51 | 
            +
                    "
         | 
| 52 | 
            +
                    hx-target="#tab-content"
         | 
| 53 | 
            +
                    hx-swap="innerHTML"
         | 
| 54 | 
            +
                    role="tab"
         | 
| 55 | 
            +
                    aria-selected="true"
         | 
| 56 | 
            +
                    aria-controls="tab-content">Keycloak Permissions</button>
         | 
| 57 | 
            +
                </li>
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                <li>
         | 
| 60 | 
            +
                  <button
         | 
| 61 | 
            +
                    id="resources-tab"
         | 
| 62 | 
            +
                    hx-get="<%= resources_path %>"
         | 
| 63 | 
            +
                    class="inline-block p-4 rounded-t-lg  border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500"
         | 
| 64 | 
            +
                    hx-on::after-request="
         | 
| 65 | 
            +
                      document.querySelector('#routes-tab').className ='inline-block p-4 rounded-t-lg border-b-2 text-gray-600 border-gray-600 dark:text-gray-500 dark:border-gray-500';
         | 
| 66 | 
            +
                      document.querySelector('#policies-tab').className = 'inline-block p-4 rounded-t-lg border-b-2 text-gray-600  border-gray-600  dark:text-gray-500 dark:border-gray-500';
         | 
| 67 | 
            +
                      document.querySelector('#permissions-tab').className = 'inline-block p-4 rounded-t-lg border-b-2 text-gray-600  border-gray-600  dark:text-gray-500 dark:border-gray-500';
         | 
| 68 | 
            +
                      document.querySelector('#resources-tab').className = 'inline-block p-4 rounded-t-lg border-b-2 text-blue-600 border-blue-600 dark:text-blue-500 dark:border-blue-500';
         | 
| 69 | 
            +
                    "
         | 
| 70 | 
            +
                    hx-target="#tab-content"
         | 
| 71 | 
            +
                    hx-swap="innerHTML"
         | 
| 72 | 
            +
                    role="tab"
         | 
| 73 | 
            +
                    aria-selected="false"
         | 
| 74 | 
            +
                    aria-controls="tab-content">Keycloak Resource</button>
         | 
| 75 | 
            +
                </li>
         | 
| 76 | 
            +
              </ul>
         | 
| 77 | 
            +
            </div>
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            <div id="tab-content" role="tabpanel" class="tab-content" hx-get="<%= routes_path %>" hx-trigger="load delay:100ms" hx-swap="innerHTML"></div>
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            <% if @permissions.empty? %>
         | 
| 2 | 
            +
              <div>No permissions created yet!</div>
         | 
| 3 | 
            +
            <% end %>
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            <% @permissions.each do |permission| %>
         | 
| 6 | 
            +
              <div>name:</div>
         | 
| 7 | 
            +
              <div><%= permission.name %></div>
         | 
| 8 | 
            +
              <div>type:</div>
         | 
| 9 | 
            +
              <div><%= permission.type %></div>
         | 
| 10 | 
            +
            <% end %>
         | 
| 11 | 
            +
             | 
| 12 | 
            +
             | 
| 13 | 
            +
            <form hx-post="<%= permissions_path %>">
         | 
| 14 | 
            +
              <div>
         | 
| 15 | 
            +
                <label for="keycloak_policy_id">Policy</label>
         | 
| 16 | 
            +
                <select name="keycloak_policy_id" hx-indicator=".htmx-indicator">
         | 
| 17 | 
            +
                  <option value="" selected disabled>Select Policy</option>
         | 
| 18 | 
            +
                  <% @policies.map do |policy| %>
         | 
| 19 | 
            +
                    <option value="<%= policy.id %>"><%= policy.name %></option>
         | 
| 20 | 
            +
                  <% end %>
         | 
| 21 | 
            +
                </select>
         | 
| 22 | 
            +
              </div>
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              <div>
         | 
| 25 | 
            +
                <label for="keycloak_resource_id">Resource</label>
         | 
| 26 | 
            +
                <select name="keycloak_resource_id"
         | 
| 27 | 
            +
                        hx-get="<%= resource_scopes_select_permissions_path %>"
         | 
| 28 | 
            +
                        hx-target="#scopes"
         | 
| 29 | 
            +
                        hx-indicator=".htmx-indicator">
         | 
| 30 | 
            +
                  <option value="" selected disabled>Select Resource</option>
         | 
| 31 | 
            +
                  <% @resources.map do |resource| %>
         | 
| 32 | 
            +
                    <option value="<%= resource.id %>"><%= resource.name %></option>
         | 
| 33 | 
            +
                  <% end %>
         | 
| 34 | 
            +
                </select>
         | 
| 35 | 
            +
              </div>
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              <div id="scopes"></div>
         | 
| 38 | 
            +
              <button
         | 
| 39 | 
            +
                class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-3 rounded"
         | 
| 40 | 
            +
                type="submit">Create</button>
         | 
| 41 | 
            +
            </form>
         | 
| @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            <label for="keycloak_scope_id">Scope</label>
         | 
| 2 | 
            +
            <select name="keycloak_scope_id" hx-indicator=".htmx-indicator">
         | 
| 3 | 
            +
              <option value="" selected disabled>Select Scope</option>
         | 
| 4 | 
            +
              <% @resource_scopes.map do |scope| %>
         | 
| 5 | 
            +
                <option value="<%= scope.id %>"><%= scope.name %></option>
         | 
| 6 | 
            +
              <% end %>
         | 
| 7 | 
            +
            </select>
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            <h1 class="text-center text-gray-500">Policies</h1>
         | 
| 2 | 
            +
            <% if @policies.empty? %>
         | 
| 3 | 
            +
              <p class="inline">Default Policy for </p>
         | 
| 4 | 
            +
              <form class="inline" hx-post="<%= policies_path %>">
         | 
| 5 | 
            +
                <input id="keycloak_policy_name" name="keycloak_policy_name" value="<%= @default_policy_name %>" type="text">
         | 
| 6 | 
            +
                <label for="keycloak_realm_role">Role</label>
         | 
| 7 | 
            +
                <select name="keycloak_realm_role_id" hx-indicator=".htmx-indicator">
         | 
| 8 | 
            +
                  <% @realm_roles.map do |role| %>
         | 
| 9 | 
            +
                    <option value="<%= role.id %>" <%= role.name.include?("default") ? "selected=selected" : "" %>>
         | 
| 10 | 
            +
                      <%= role.name %>
         | 
| 11 | 
            +
                    </option>
         | 
| 12 | 
            +
                  <% end %>
         | 
| 13 | 
            +
                </select>
         | 
| 14 | 
            +
                <button
         | 
| 15 | 
            +
                  class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-3 rounded"
         | 
| 16 | 
            +
                  type="submit">Create</button>
         | 
| 17 | 
            +
              </form>
         | 
| 18 | 
            +
            <% else %>
         | 
| 19 | 
            +
                <div class="text-gray-500">Current Policy</div>
         | 
| 20 | 
            +
                <% @policies.map do |policy| %>
         | 
| 21 | 
            +
                 <p><%= policy.name %></p>
         | 
| 22 | 
            +
                <% end %>
         | 
| 23 | 
            +
            <% end %>
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            <% if @keycloak_resource %>
         | 
| 2 | 
            +
              <table class="table-auto">
         | 
| 3 | 
            +
                <thead>
         | 
| 4 | 
            +
                <tr><th>Controller</th></tr>
         | 
| 5 | 
            +
                </thead>
         | 
| 6 | 
            +
                <tbody>
         | 
| 7 | 
            +
                <tr><td><%= @keycloak_resource.name %></td></tr>
         | 
| 8 | 
            +
                </tbody>
         | 
| 9 | 
            +
              </table>
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              <table class="table-auto">
         | 
| 12 | 
            +
                <thead>
         | 
| 13 | 
            +
                <tr>
         | 
| 14 | 
            +
                  <th>Scope</th>
         | 
| 15 | 
            +
                </tr>
         | 
| 16 | 
            +
                </thead>
         | 
| 17 | 
            +
                <tbody>
         | 
| 18 | 
            +
                <tr>
         | 
| 19 | 
            +
                  <td>
         | 
| 20 | 
            +
                    <div hx-get="<%= scope_path("scope:#{@route.defaults[:action]}", keycloak_resource_id: @keycloak_resource.id, keycloak_scope_name: @route.defaults[:action]) %>" hx-trigger="load" ></div>
         | 
| 21 | 
            +
                  </td>
         | 
| 22 | 
            +
                </tr>
         | 
| 23 | 
            +
                </tbody>
         | 
| 24 | 
            +
              </table>
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            <% else %>
         | 
| 27 | 
            +
              <div hx-get="<%= new_resource_path(route_id: @route.name) %>" hx-trigger="load" ></div>
         | 
| 28 | 
            +
            <% end %>
         | 
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            <table>
         | 
| 2 | 
            +
              <caption>Available Routes</caption>
         | 
| 3 | 
            +
              <thead>
         | 
| 4 | 
            +
              <tr>
         | 
| 5 | 
            +
                <th scope="col" class="text-left">Verb</th>
         | 
| 6 | 
            +
                <th scope="col" class="text-left">Name</th>
         | 
| 7 | 
            +
                <th scope="col" class="text-left">Path</th>
         | 
| 8 | 
            +
                <th scope="col" class="text-left">Controller</th>
         | 
| 9 | 
            +
                <th scope="col" class="text-left">Action</th>
         | 
| 10 | 
            +
                <th scope="col" class="text-left">Inspect</th>
         | 
| 11 | 
            +
              </tr>
         | 
| 12 | 
            +
              </thead>
         | 
| 13 | 
            +
              <% @routes.each do |route| %>
         | 
| 14 | 
            +
                <tbody>
         | 
| 15 | 
            +
                <tr class="p-2 bg-gray-100 border border-gray-200">
         | 
| 16 | 
            +
                  <td class="text-left">
         | 
| 17 | 
            +
                    <%= route.verb %>
         | 
| 18 | 
            +
                  </td>
         | 
| 19 | 
            +
                  <td class="text-left">
         | 
| 20 | 
            +
                    <%= route.name %>
         | 
| 21 | 
            +
                  </td>
         | 
| 22 | 
            +
                  <td class="text-left">
         | 
| 23 | 
            +
                    <%= route.path.spec.to_s %>
         | 
| 24 | 
            +
                  </td>
         | 
| 25 | 
            +
                  <td class="text-left">
         | 
| 26 | 
            +
                    <%= route.defaults[:controller] %>
         | 
| 27 | 
            +
                  </td>
         | 
| 28 | 
            +
                  <td class="text-left">
         | 
| 29 | 
            +
                    <%= route.defaults[:action] %>
         | 
| 30 | 
            +
                  </td>
         | 
| 31 | 
            +
                  <td class="text-left">
         | 
| 32 | 
            +
                    <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-3 rounded"
         | 
| 33 | 
            +
                            popovertarget="modal"
         | 
| 34 | 
            +
                            hx-get="<%= route_path(route.name) %>"
         | 
| 35 | 
            +
                            hx-trigger="click"
         | 
| 36 | 
            +
                            hx-target="#modal-content"
         | 
| 37 | 
            +
                            hx-swap="innserHtml">Inspect</button>
         | 
| 38 | 
            +
                  </td>
         | 
| 39 | 
            +
                </tr>
         | 
| 40 | 
            +
                <tbody>
         | 
| 41 | 
            +
              <% end %>
         | 
| 42 | 
            +
            </table>
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            <div popover id="modal" class="border border-gray-200">
         | 
| 45 | 
            +
              <style>
         | 
| 46 | 
            +
                  :popover-open {
         | 
| 47 | 
            +
                      width: 600px;
         | 
| 48 | 
            +
                      height: 400px;
         | 
| 49 | 
            +
                  }
         | 
| 50 | 
            +
              </style>
         | 
| 51 | 
            +
              <div class="bg-gray-200 h-5/6 w-full p-4" id="modal-content" >
         | 
| 52 | 
            +
                <h1>Modal Dialog</h1>
         | 
| 53 | 
            +
                This is the modal content. You can put anything here, like text, or a form, or an image.
         | 
| 54 | 
            +
              </div>
         | 
| 55 | 
            +
              <div class="text-center pt-2">
         | 
| 56 | 
            +
                <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-3 rounded" hx-on:click="document.getElementById('modal').hidePopover()">Close</button>
         | 
| 57 | 
            +
              </div>
         | 
| 58 | 
            +
            </div>
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            <div id="scope">
         | 
| 2 | 
            +
              <% if @resource_scope %>
         | 
| 3 | 
            +
                <div>Resource Attached Scope: <b><%= @resource_scope.name %></b></div>
         | 
| 4 | 
            +
                <div>Check <b>Keycloak Policies</b> tab</div>
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              <% elsif @available_scope %>
         | 
| 7 | 
            +
                Available Scope to attach: <%= @available_scope.name %>
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                <button
         | 
| 10 | 
            +
                  class="block bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-3 rounded"
         | 
| 11 | 
            +
                  hx-post="<%= attach_scope_path(@keycloak_scope_name) %>"
         | 
| 12 | 
            +
                  hx-target="#scope"
         | 
| 13 | 
            +
                  hx-vals='{
         | 
| 14 | 
            +
                          "keycloak_scope_name": "<%= @keycloak_scope_name %>",
         | 
| 15 | 
            +
                          "keycloak_resource_id": "<%= @keycloak_resource_id %>"
         | 
| 16 | 
            +
                        }'
         | 
| 17 | 
            +
                >
         | 
| 18 | 
            +
                  Attach scope <%= @keycloak_scope_name %> to resource ?
         | 
| 19 | 
            +
                </button>
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              <% else %>
         | 
| 22 | 
            +
                Scope <b><%= @keycloak_scope_name %></b> not created yet.
         | 
| 23 | 
            +
                <button
         | 
| 24 | 
            +
                  class="block bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-3 rounded"
         | 
| 25 | 
            +
                  hx-post="<%= scopes_path %>"
         | 
| 26 | 
            +
                  hx-target="#scope"
         | 
| 27 | 
            +
                  hx-vals='{
         | 
| 28 | 
            +
                          "keycloak_scope_name": "<%= @keycloak_scope_name %>",
         | 
| 29 | 
            +
                          "keycloak_resource_id": "<%= @keycloak_resource_id %>"
         | 
| 30 | 
            +
                        }'
         | 
| 31 | 
            +
                >Create Scope?</button>
         | 
| 32 | 
            +
              <% end %>
         | 
| 33 | 
            +
            </div>
         | 
    
        data/config/routes.rb
    ADDED
    
    | @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            RailsKeycloakAuthorization::Engine.routes.draw do
         | 
| 2 | 
            +
              root "management#index"
         | 
| 3 | 
            +
              resources :management, only: [:index]
         | 
| 4 | 
            +
              resources :routes, only: [:index, :show]
         | 
| 5 | 
            +
              resources :policies, only: [:index, :create]
         | 
| 6 | 
            +
              resources :permissions, only: [:index, :create] do
         | 
| 7 | 
            +
                get :resource_scopes_select, on: :collection
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
              resources :resources, only: [:create, :show, :new, :index]
         | 
| 10 | 
            +
              resources :scopes, only: [:index, :show, :new, :create] do
         | 
| 11 | 
            +
                post :attach, on: :member
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| @@ -0,0 +1,77 @@ | |
| 1 | 
            +
            require "rails_keycloak_authorization/version"
         | 
| 2 | 
            +
            require "rails_keycloak_authorization/engine"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module RailsKeycloakAuthorization
         | 
| 5 | 
            +
              mattr_accessor :keycloak_realm
         | 
| 6 | 
            +
              mattr_accessor :keycloak_server_url
         | 
| 7 | 
            +
              mattr_accessor :keycloak_admin_username
         | 
| 8 | 
            +
              mattr_accessor :keycloak_admin_password
         | 
| 9 | 
            +
              mattr_accessor :match_patterns
         | 
| 10 | 
            +
              mattr_accessor :client_id
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              class Middleware
         | 
| 13 | 
            +
                def initialize(app)
         | 
| 14 | 
            +
                  @app = app
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def call(env)
         | 
| 18 | 
            +
                  if should_process?(env["REQUEST_URI"],)
         | 
| 19 | 
            +
                    if !env["HTTP_AUTHORIZATION"]
         | 
| 20 | 
            +
                      [403, {}, ["Authentication Failed"]]
         | 
| 21 | 
            +
                    elsif authorize!(env['REQUEST_URI'], env['HTTP_AUTHORIZATION'])
         | 
| 22 | 
            +
                      @app.call(env)
         | 
| 23 | 
            +
                    else
         | 
| 24 | 
            +
                      [403, {}, ["Authorization Failed"]]
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                  else
         | 
| 27 | 
            +
                    @app.call(env)
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def should_process?(request_uri)
         | 
| 32 | 
            +
                  RailsKeycloakAuthorization.match_patterns.detect do |r|
         | 
| 33 | 
            +
                    r.match(request_uri)
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def authorize!(request_uri, http_authorization)
         | 
| 38 | 
            +
                  route = Rails.application.routes.recognize_path(request_uri)
         | 
| 39 | 
            +
                  uri = uri(RailsKeycloakAuthorization.keycloak_server_url, RailsKeycloakAuthorization.keycloak_realm)
         | 
| 40 | 
            +
                  request = http_request(uri, http_authorization, route)
         | 
| 41 | 
            +
                  response = http_client(uri).request(request)
         | 
| 42 | 
            +
                  response.is_a?(Net::HTTPSuccess)
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def http_request(uri, http_authorization, route)
         | 
| 46 | 
            +
                  request = Net::HTTP::Post.new(uri, {
         | 
| 47 | 
            +
                    'Content-Type' => 'application/x-www-form-urlencoded',
         | 
| 48 | 
            +
                    'Authorization' => http_authorization,
         | 
| 49 | 
            +
                  })
         | 
| 50 | 
            +
                  permission = "#{route[:controller]}_controller##{route[:action]}"
         | 
| 51 | 
            +
                  request.body = URI.encode_www_form({
         | 
| 52 | 
            +
                                                       audience: "#{RailsKeycloakAuthorization.client_id}",
         | 
| 53 | 
            +
                                                       grant_type: grant_type,
         | 
| 54 | 
            +
                                                       permission: permission,
         | 
| 55 | 
            +
                                                       response_mode: "permissions",
         | 
| 56 | 
            +
                                                       permission_resource_format: "id",
         | 
| 57 | 
            +
                                                       permission_resource_matching_uri: false
         | 
| 58 | 
            +
                                                     })
         | 
| 59 | 
            +
                  request
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def grant_type
         | 
| 63 | 
            +
                  "urn:ietf:params:oauth:grant-type:uma-ticket"
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def uri(keycloak_server_url, keycloak_realm)
         | 
| 67 | 
            +
                  URI("#{keycloak_server_url}/realms/#{keycloak_realm}/protocol/openid-connect/token")
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                def http_client(uri)
         | 
| 71 | 
            +
                  http = Net::HTTP.new(uri.host, uri.port)
         | 
| 72 | 
            +
                  http.use_ssl = Rails.env.production?
         | 
| 73 | 
            +
                  http.read_timeout = ENV.fetch("KEYCLOAK_AUTHORIZATION_TIMEOUT", 1).to_i
         | 
| 74 | 
            +
                  http
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
              end
         | 
| 77 | 
            +
            end
         | 
| @@ -0,0 +1,170 @@ | |
| 1 | 
            +
            require 'keycloak-admin'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            namespace :'rails-keycloak-authorization' do
         | 
| 4 | 
            +
              desc "Explaining what the task does"
         | 
| 5 | 
            +
              task :create_default_scopes do
         | 
| 6 | 
            +
                keycloak_admin_configure
         | 
| 7 | 
            +
                realm_name = ENV["KEYCLOAK_AUTH_CLIENT_REALM_NAME"]
         | 
| 8 | 
            +
                client = KeycloakAdmin.realm(realm_name).clients.find_by_client_id(ENV["KEYCLOAK_AUTH_CLIENT_ID"])
         | 
| 9 | 
            +
                KeycloakAdmin.realm(realm_name).authz_scopes(client.id).create!("POST", "POST Scope", "")
         | 
| 10 | 
            +
                KeycloakAdmin.realm(realm_name).authz_scopes(client.id).create!("GET", "GET Scope", "")
         | 
| 11 | 
            +
                KeycloakAdmin.realm(realm_name).authz_scopes(client.id).create!("PUT", "PUT Scope", "")
         | 
| 12 | 
            +
                KeycloakAdmin.realm(realm_name).authz_scopes(client.id).create!("PATCH", "PATCH Scope", "")
         | 
| 13 | 
            +
                KeycloakAdmin.realm(realm_name).authz_scopes(client.id).create!("DELETE", "DESTROY Scope", "")
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              desc "Explaining what the task does"
         | 
| 17 | 
            +
              task :create_resources do
         | 
| 18 | 
            +
                create_resource "Organization"
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              desc "Explaining what the task does"
         | 
| 22 | 
            +
              task :create_policy do
         | 
| 23 | 
            +
                create_policy
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              desc "Explaining what the task does"
         | 
| 27 | 
            +
              task :create_permission do
         | 
| 28 | 
            +
                create_permission
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
             | 
| 32 | 
            +
              desc "Explaining what the task does"
         | 
| 33 | 
            +
              task :validate_keycloak_admin_ruby_permissions do
         | 
| 34 | 
            +
                puts "Validating, Rails::Keycloak::Authorization ...."
         | 
| 35 | 
            +
                keycloak_admin_configure
         | 
| 36 | 
            +
                realm_name = ENV["KEYCLOAK_AUTH_CLIENT_REALM_NAME"]
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                client = KeycloakAdmin.realm(realm_name).clients.find_by_client_id(ENV["KEYCLOAK_AUTH_CLIENT_ID"])
         | 
| 39 | 
            +
                client.authorization_services_enabled = true
         | 
| 40 | 
            +
                KeycloakAdmin.realm(realm_name).clients.update(client)
         | 
| 41 | 
            +
                KeycloakAdmin.realm(realm_name).authz_scopes(client.id).list.each{|scope| puts scope.name }
         | 
| 42 | 
            +
                KeycloakAdmin.realm(realm_name).authz_resources(client.id).list.each{|scope| puts scope.uris }
         | 
| 43 | 
            +
                KeycloakAdmin.realm(realm_name).authz_policies(client.id, 'role').list.each{|scope| puts "Policy #{scope.name}" }
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                realm_role =  KeycloakAdmin.realm(realm_name).roles.get("default-roles-dummy")
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                scope_1 = KeycloakAdmin.realm(realm_name).authz_scopes(client.id).create!("POST_1", "POST 1 scope", "http://asdas")
         | 
| 48 | 
            +
                scope_2 = KeycloakAdmin.realm(realm_name).authz_scopes(client.id).create!("POST_2", "POST 2 scope", "http://asdas")
         | 
| 49 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_scopes(client.id).search("POST")
         | 
| 50 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_scopes(client.id).get(scope_1.id)
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                # resource = KeycloakAdmin.realm(realm_name).authz_resources(client.id).create!(
         | 
| 53 | 
            +
                #   "Dummy Resource",
         | 
| 54 | 
            +
                #   "type",
         | 
| 55 | 
            +
                #   ["/asdf/*", "/tmp/"],
         | 
| 56 | 
            +
                #   true,
         | 
| 57 | 
            +
                #   "display_name",
         | 
| 58 | 
            +
                #   [{id: scope_1.id, name: scope_1.name},{id: scope_2.id, name: scope_2.name}],
         | 
| 59 | 
            +
                #   {"a": ["b", "c"]}
         | 
| 60 | 
            +
                # )
         | 
| 61 | 
            +
                resource = KeycloakAdmin.realm(realm_name).authz_resources(client.id).create!("Dummy Resource", "type", ["/asdf/*", "/tmp/"], true, "display_name", [], {"a": ["b", "c"]})
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_resources(client.id).find_by("Dummy Resource", "", "", "", "").first.name
         | 
| 64 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_resources(client.id).find_by("", "type", "", "", "").first.name
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_resources(client.id).get(resource.id).scopes.count
         | 
| 67 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_resources(client.id).get(resource.id).uris.count
         | 
| 68 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_resources(client.id).update(resource.id,
         | 
| 69 | 
            +
                                                                                       {
         | 
| 70 | 
            +
                                                                                         "name": "Dummy Resource",
         | 
| 71 | 
            +
                                                                                         "type": "type",
         | 
| 72 | 
            +
                                                                                         "owner_managed_access": true,
         | 
| 73 | 
            +
                                                                                         "display_name": "display_name",
         | 
| 74 | 
            +
                                                                                         "attributes": {"a":["b","c"]},
         | 
| 75 | 
            +
                                                                                         "uris": [ "/asdf/*" , "/tmp/45" ],
         | 
| 76 | 
            +
                                                                                         "scopes":[
         | 
| 77 | 
            +
                                                                                           {name: scope_1.name},{name: scope_2.name}
         | 
| 78 | 
            +
                                                                                         ],
         | 
| 79 | 
            +
                                                                                         "icon_uri": "https://icon.ico"
         | 
| 80 | 
            +
                                                                                       }
         | 
| 81 | 
            +
                                                                                       )
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_resources(client.id).update(resource.id,
         | 
| 84 | 
            +
                                                                                       {
         | 
| 85 | 
            +
                                                                                         "name": "Dummy Resource",
         | 
| 86 | 
            +
                                                                                         "scopes":[
         | 
| 87 | 
            +
                                                                                           {name: scope_1.name}
         | 
| 88 | 
            +
                                                                                         ]
         | 
| 89 | 
            +
                                                                                       }
         | 
| 90 | 
            +
                )
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                policy = KeycloakAdmin.realm(realm_name).authz_policies(client.id, 'role').create!("Policy 1", "description", "role", "POSITIVE", "UNANIMOUS", true, [{id: realm_role.id, required: true}])
         | 
| 93 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_policies(client.id, 'role').find_by("Policy 1", "role").first.name
         | 
| 94 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_policies(client.id, 'role').get(policy.id).name
         | 
| 95 | 
            +
                scope_permission = KeycloakAdmin.realm(realm_name).authz_permissions(client.id, :scope).create!("Dummy Scope Permission", "scope description", "UNANIMOUS", "POSITIVE", [resource.id], [policy.id], [scope_1.id, scope_2.id], "")
         | 
| 96 | 
            +
                resource_permission = KeycloakAdmin.realm(realm_name).authz_permissions(client.id, :resource).create!("Dummy Resource Permission", "resource description", "UNANIMOUS", "POSITIVE", [resource.id], [policy.id], nil, "")
         | 
| 97 | 
            +
                resource_permissions = KeycloakAdmin.realm(realm_name).authz_permissions(client.id, "", resource.id).list
         | 
| 98 | 
            +
                puts resource_permissions.length
         | 
| 99 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_permissions(client.id, "resource").get(resource_permission.id)
         | 
| 100 | 
            +
                debugger
         | 
| 101 | 
            +
                resource_scopes = KeycloakAdmin.realm(realm_name).authz_scopes(client.id, resource.id).list
         | 
| 102 | 
            +
                puts resource_scopes.length
         | 
| 103 | 
            +
                KeycloakAdmin.realm(realm_name).authz_permissions(client.id, 'scope').list.map{|r| puts r.name}
         | 
| 104 | 
            +
                KeycloakAdmin.realm(realm_name).authz_permissions(client.id, 'resource').list.map{|r| puts r.name}
         | 
| 105 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_permissions(client.id, "resource").find_by(resource_permission.name, nil).first.name
         | 
| 106 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_permissions(client.id, "resource").find_by(resource_permission.name, resource.id).first.name
         | 
| 107 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_permissions(client.id, "scope").find_by(scope_permission.name, resource.id).first.name
         | 
| 108 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_permissions(client.id, "scope").find_by(scope_permission.name, resource.id, "POST_1").first.name
         | 
| 109 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_permissions(client.id, "resource").find_by(nil, resource.id).first.name
         | 
| 110 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_permissions(client.id, "scope").find_by(nil, resource.id).first.name
         | 
| 111 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_permissions(client.id, "scope").find_by(nil, resource.id, "POST_1").first.name
         | 
| 112 | 
            +
                puts KeycloakAdmin.realm(realm_name).authz_permissions(client.id, "scope").find_by(scope_permission.name, nil).first.name
         | 
| 113 | 
            +
                # KeycloakAdmin.realm(realm_name).authz_permissions(client.id).find_by(resource_permission.name, resource.id, "scope", nil )
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                KeycloakAdmin.realm(realm_name).authz_permissions(client.id, 'scope').delete(scope_permission.id)
         | 
| 116 | 
            +
                KeycloakAdmin.realm(realm_name).authz_permissions(client.id, 'resource').delete(resource_permission.id)
         | 
| 117 | 
            +
                KeycloakAdmin.realm(realm_name).authz_policies(client.id, 'role').delete(policy.id)
         | 
| 118 | 
            +
                KeycloakAdmin.realm(realm_name).authz_resources(client.id).delete(resource.id)
         | 
| 119 | 
            +
                KeycloakAdmin.realm(realm_name).authz_scopes(client.id).delete(scope_1.id)
         | 
| 120 | 
            +
                KeycloakAdmin.realm(realm_name).authz_scopes(client.id).delete(scope_2.id)
         | 
| 121 | 
            +
                puts "Validation, Rails::Keycloak::Authorization done."
         | 
| 122 | 
            +
              end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
             | 
| 125 | 
            +
             | 
| 126 | 
            +
             | 
| 127 | 
            +
             | 
| 128 | 
            +
              def create_resource(model_name)
         | 
| 129 | 
            +
                keycloak_admin_configure
         | 
| 130 | 
            +
                resource_name = model_name.downcase.pluralize
         | 
| 131 | 
            +
                client = KeycloakAdmin.realm(realm_name).clients.find_by_client_id(ENV.fetch("KEYCLOAK_AUTH_CLIENT_ID"))
         | 
| 132 | 
            +
                scopes = KeycloakAdmin.realm(realm_name).authz_scopes(client.id).list.reverse
         | 
| 133 | 
            +
                KeycloakAdmin.realm(realm_name)
         | 
| 134 | 
            +
                             .authz_resources(client.id)
         | 
| 135 | 
            +
                             .create!(model_name,
         | 
| 136 | 
            +
                                      "type",
         | 
| 137 | 
            +
                                      ["/#{resource_name}/*", "/#{resource_name}"],
         | 
| 138 | 
            +
                                      true,
         | 
| 139 | 
            +
                                      "#{resource_name.capitalize} Resource",
         | 
| 140 | 
            +
                                      scopes.map{|scope| {id: scope.id, name: scope.name} },
         | 
| 141 | 
            +
                                      { "model": resource_name }
         | 
| 142 | 
            +
                             )
         | 
| 143 | 
            +
              end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
              def create_policy
         | 
| 146 | 
            +
                keycloak_admin_configure
         | 
| 147 | 
            +
                realm_role =  KeycloakAdmin.realm(realm_name).roles.get("default-roles-dummy")
         | 
| 148 | 
            +
                client = KeycloakAdmin.realm(realm_name).clients.find_by_client_id(ENV["KEYCLOAK_AUTH_CLIENT_ID"])
         | 
| 149 | 
            +
                KeycloakAdmin
         | 
| 150 | 
            +
                  .realm(realm_name)
         | 
| 151 | 
            +
                  .authz_policies(client.id, 'role')
         | 
| 152 | 
            +
                  .create!("Policy 1",
         | 
| 153 | 
            +
                           "description",
         | 
| 154 | 
            +
                           "role",
         | 
| 155 | 
            +
                           "POSITIVE",
         | 
| 156 | 
            +
                           "UNANIMOUS",
         | 
| 157 | 
            +
                           true,
         | 
| 158 | 
            +
                           [{id: realm_role.id, required: true}]
         | 
| 159 | 
            +
                  )
         | 
| 160 | 
            +
              end
         | 
| 161 | 
            +
              def create_permission
         | 
| 162 | 
            +
                keycloak_admin_configure
         | 
| 163 | 
            +
                client = KeycloakAdmin.realm(realm_name).clients.find_by_client_id(ENV["KEYCLOAK_AUTH_CLIENT_ID"])
         | 
| 164 | 
            +
                resource = KeycloakAdmin.realm(realm_name).authz_resources(client.id).find_by(client.id, "Organization", "", "", "").first
         | 
| 165 | 
            +
                policy = KeycloakAdmin.realm(realm_name).authz_policies( client.id, 'role').find_by("Policy 1", "role").first
         | 
| 166 | 
            +
                scope_permission = KeycloakAdmin.realm(realm_name).authz_permissions(client.id, :scope).create!("Organizations Scope Permission", "scope description", "UNANIMOUS", "POSITIVE", [resource.id], [policy.id], ["POST", "GET", "PATCH", "PUT", "DELETE"], "")
         | 
| 167 | 
            +
              end
         | 
| 168 | 
            +
             | 
| 169 | 
            +
             | 
| 170 | 
            +
            end
         |