Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RBAC #111

Merged
merged 8 commits into from
Dec 8, 2022
Merged

RBAC #111

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
inherit_from: .rubocop_todo.yml

require: rubocop-rails
require:
- rubocop-rails

AllCops:
TargetRubyVersion: 2.5
Expand All @@ -12,6 +13,7 @@ AllCops:
- bin/*
- config/application.rb
- config/environments/*
- db/schema.rb
- vendor/**/*
- .vendor/**/*
Naming/PredicateName:
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ def authentication_required

def load_environments
@environments = Environment.all
@environments.select! { |e| current_user.may_access?(e) }
@environment = Environment.find(params[:environment_id])
authorize! :show, @environment
end

def display_error_page(error)
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/environments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ class EnvironmentsController < ApplicationController
add_breadcrumb "Environments", :environments_path

def index
@environments = Environment.all
authorize! :index, Environment
@environments = Environment.all
@environments.select! { |e| current_user.may_access?(e) }
end
end
7 changes: 6 additions & 1 deletion app/controllers/files_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ class FilesController < ApplicationController
add_breadcrumb "Environments", :environments_path

def index
@files_and_values_by_hierarchy = DataFile.search(@environment, @key)
@files_and_values_by_hierarchy =
if can? :show, @key
DataFile.search(@environment, @key)
else
{}
end

add_breadcrumb @environment, environment_nodes_path(@environment)
add_breadcrumb @key, environment_key_files_path(@environment, @key)
Expand Down
24 changes: 24 additions & 0 deletions app/controllers/group_memberships_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class GroupMembershipsController < ApplicationController
load_and_authorize_resource :group
add_breadcrumb "Home", :root_path
add_breadcrumb "Groups", :groups_path

def edit
add_breadcrumb @group.name, group_path(@group)
add_breadcrumb "Edit Memberships", edit_group_group_memberships_path(@group)
end

def update
if @group.update(group_params)
redirect_to @group, notice: "Group memberships were updated successfully."
else
render action: "edit"
end
end

private

def group_params
params.require(:group).permit(user_ids: [])
end
end
60 changes: 60 additions & 0 deletions app/controllers/groups_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class GroupsController < ApplicationController
load_and_authorize_resource
add_breadcrumb "Home", :root_path

def index
add_breadcrumb "Groups", :groups_path
@groups = @groups.order(:name)
end

def show
add_breadcrumb "Groups", :groups_path
add_breadcrumb @group.name, group_path(@group)
end

def new
add_breadcrumb "Groups", :groups_path
add_breadcrumb "New Group", new_group_path
@group = Group.new
end

def create
@group = Group.new(group_params)

if @group.save
redirect_to @group, notice: "Group was created successfully."
else
render action: "new"
end
end

def edit
add_breadcrumb "Groups", :groups_path
add_breadcrumb @group.name, group_path(@group)
add_breadcrumb "Edit", edit_group_path(@group)
end

def update
if @group.update(group_params)
redirect_to @group, notice: "Group was updated successfully."
else
render action: "edit"
end
end

def destroy
@group.destroy

redirect_to groups_path, notice: "Group was deleted successfully"
end

private

def group_params
permitted_params = params
.require(:group)
.permit(:name, :restrict, rules: [])
permitted_params[:rules] ||= []
permitted_params
end
end
12 changes: 7 additions & 5 deletions app/controllers/keys_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,35 @@ class KeysController < ApplicationController
def index
authorize! :show, Key
@keys = Key.all_for(@node)
@keys.select! { |k| current_user.may_access?(k) }

add_breadcrumb @environment, environment_nodes_path(@environment)
add_breadcrumb @node, environment_node_keys_path(@environment, @node)
end

def show
authorize! :show, Key
@keys = Key.all_for(@node)
@keys.select! { |k| current_user.may_access?(k) }
@key = Key.new(environment: @environment, name: params[:id])
authorize! :show, @key

add_breadcrumb @environment, environment_nodes_path(@environment)
add_breadcrumb @node, environment_node_keys_path(@environment, @node)
add_breadcrumb params[:id], environment_node_key_path(@environment, @node, params[:id])
end

def update
authorize! :update, Key

@key = Key.new(@node, params[:id])
authorize! :update, @key
@key.save_value(params[:hierarchy], params[:path], params[:value])

redirect_to environment_node_key_path(@environment, @node, @key),
notice: "Value was saved successfully"
end

def destroy
authorize! :destroy, Key

@key = Key.new(@node, params[:id])
authorize! :destroy, @key
@key.remove_value(params[:hierarchy], params[:path])

redirect_to environment_node_key_path(@environment, @node, @key),
Expand All @@ -47,6 +47,8 @@ def destroy

def load_nodes
@nodes = Node.all(environment: @environment)
@nodes.select! { |n| current_user.may_access?(n) }
@node = Node.new(hostname: params[:node_id], environment: @environment)
authorize! :show, @node
end
end
3 changes: 2 additions & 1 deletion app/controllers/nodes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ class NodesController < ApplicationController
add_breadcrumb "Environments", :environments_path

def index
@nodes = Node.all(environment: @environment)
authorize! :index, Node
@nodes = Node.all(environment: @environment)
@nodes.select! { |n| current_user.may_access?(n) }

add_breadcrumb @environment, environment_nodes_path(@environment)
end
Expand Down
11 changes: 11 additions & 0 deletions app/javascript/controllers/append_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Controller } from "stimulus"

export default class extends Controller {
static targets = ["container", "template"];

append(event) {
event.preventDefault();
const template = this.templateTarget.innerHTML;
this.containerTarget.insertAdjacentHTML('beforeend', template);
}
}
8 changes: 8 additions & 0 deletions app/javascript/controllers/remove_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Controller } from "stimulus"

export default class extends Controller {
remove(event) {
event.preventDefault();
this.element.parentNode.removeChild(this.element);
}
}
15 changes: 12 additions & 3 deletions app/models/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
class Ability
include CanCan::Ability

# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def initialize(user)
# Define abilities for the passed in user here. For example:
#
Expand Down Expand Up @@ -44,16 +45,24 @@ def initialize(user)
end
can :manage, User, id: User.where.not(id: user.id).ids
can :create, User, id: nil
can :manage, Group
end

if user.user?
can :read, User, id: user.id
can :update, User, id: user.id
can :manage, Environment
can :manage, Node
can :manage, Key
can :manage, Environment do |environment|
user.may_access?(environment)
end
can :manage, Node do |node|
user.may_access?(node)
end
can :manage, Key do |key|
user.may_access?(key)
end
can :manage, Value
end
end
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
end
8 changes: 8 additions & 0 deletions app/models/application_record.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true

before_destroy :ensure_destruction_possible

private

def ensure_destruction_possible
throw :abort if respond_to?(:destroyable?) && !destroyable?
end
end
24 changes: 24 additions & 0 deletions app/models/concerns/has_groups.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module HasGroups
extend ActiveSupport::Concern

included do
has_many :group_memberships, dependent: :destroy
has_many :groups, through: :group_memberships
end

def full_name_with_email
"#{first_name} #{last_name} <#{email}>"
end

def may_access?(record)
return true if groups.none?

restrictable = record.class.name.downcase
raise "Unknown entity #{record.class}" unless restrictable.in? Group::RESTRICTABLES

applicable_groups = groups.where(restrict: restrictable)
return true if applicable_groups.none?

applicable_groups.any? { |g| g.may_access?(record) }
end
end
4 changes: 4 additions & 0 deletions app/models/dummy_user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ def admin?
def user?
true
end

def may_access?(_record)
true
end
end
29 changes: 29 additions & 0 deletions app/models/group.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class Group < ApplicationRecord
RESTRICTABLES = %w[environment node key].freeze

serialize :rules, Array

has_many :group_memberships, dependent: :destroy
has_many :users, through: :group_memberships

validates :name, presence: true, uniqueness: true
validates :restrict, inclusion: RESTRICTABLES
validates :rules, regexp: true, length: { minimum: 1 }

def destroyable?
group_memberships.none?
end

def may_access?(record)
record_class = record.class.name.downcase
raise "Cannot check #{record_class}" unless record_class == restrict

compiled_rules.any? { |r| r.match(record.name) }
end

private

def compiled_rules
@compiled_rules ||= rules.map { |r| Regexp.new(r) }
end
end
4 changes: 4 additions & 0 deletions app/models/group_membership.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class GroupMembership < ApplicationRecord
belongs_to :user
belongs_to :group
end
1 change: 1 addition & 0 deletions app/models/node.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class Node < HieraModel
attribute :hostname, :string
attribute :environment
alias name hostname

def self.all_names(environment:)
PuppetDbClient.nodes(environment: environment)
Expand Down
2 changes: 2 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class User < ApplicationRecord
include HasGroups

has_secure_password validations: false

before_validation :downcase_email, on: [:create, :update]
Expand Down
9 changes: 9 additions & 0 deletions app/validators/regexp_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class RegexpValidator < ActiveModel::EachValidator
def validate_each(record, attribute, values)
Array(values).each do |value|
Regexp.new(value)
end
rescue RegexpError
record.errors.add(attribute, :invalid)
end
end
2 changes: 1 addition & 1 deletion app/views/files/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<% else %>
Could not find key
<code><%= @key.name %></code>
in any files.
in any files, or you are not allowed to access this key.
<% end %>
</p>
</div>
Expand Down
Loading