All Files (86.6% covered at 15.03 hits/line)
52 files in total.
1239 relevant lines.
1073 lines covered and
166 lines missed
-
# frozen_string_literal: true
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
# Action represents single action required to be taken to converge
-
# system into target state. Actions are created during planning phase
-
# and then run by feeding in current resource they are called from within
-
3
class Action
-
3
attr_accessor :class_name
-
-
# rubocop:disable Lint/UnusedMethodArgument
-
3
def apply(resource)
-
raise 'Abstract method left behind'
-
end
-
# rubocop:enable Lint/UnusedMethodArgument
-
-
3
protected
-
-
3
def noop
-
::Chef::Log.debug('Noop action')
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
class Create < Action
-
3
attr_accessor :account
-
-
# @param [AMA::Chef::User::Model::Account] account
-
3
def initialize(account)
-
16
@account = account
-
16
super()
-
end
-
-
3
def apply(resource_factory)
-
resource_factory.user @account.id.to_s
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
# rubocop:disable Metrics/AbcSize
-
-
3
require_relative '../../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module PrivateKey
-
3
class Add < Action
-
3
attr_accessor :account
-
3
attr_accessor :key
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::PrivateKey] key
-
3
def initialize(account, key)
-
3
@account = account
-
3
@key = key
-
end
-
-
3
def apply(resource_factory)
-
account = @account
-
key = @key
-
name = "#{account.id}:#{key.owner}:#{key.id}"
-
resource_factory.ssh_private_key name do
-
id key.id.to_s
-
user account.id.to_s
-
content key.content.to_s
-
install_public_key key.install_public_key
-
passphrase key.passphrase unless key.passphrase.nil?
-
perform_validation key.validate
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module PrivateKey
-
3
class Purge < Action
-
3
attr_accessor :account
-
-
# @param [AMA::Chef::User::Model::Account] account
-
3
def initialize(account)
-
27
@account = account
-
end
-
-
3
def apply(*)
-
noop
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../../action'
-
3
require_relative '../../../../helper/ssh_methods'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module PrivateKey
-
3
module Remote
-
3
class Add < Action
-
3
include Helper::SSHMethods
-
-
3
attr_accessor :account
-
3
attr_accessor :private_key
-
3
attr_accessor :remote
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::PrivateKey] private_key
-
# @param [AMA::Chef::User::Model::PrivateKey::Remote] remote
-
3
def initialize(account, private_key, remote)
-
2
@account = account
-
2
@private_key = private_key
-
2
@remote = remote
-
end
-
-
3
def apply(resource_factory)
-
path = "#{ssh_directory(@account.id)}/#{@private_key.id}"
-
options = @remote.options.merge('IdentityFile' => path)
-
account = @account
-
key = @private_key
-
remote = @remote
-
name = "#{account.id}:#{key.owner}:#{key.id}:#{remote.id}"
-
resource_factory.ssh_config name do
-
user account.id.to_s
-
host remote.id.to_s
-
options options
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module PrivateKey
-
3
module Remote
-
3
class Purge < Action
-
3
attr_accessor :account
-
3
attr_accessor :private_key
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::PrivateKey] private_key
-
3
def initialize(account, private_key)
-
6
@account = account
-
6
@private_key = private_key
-
end
-
-
3
def apply(*)
-
noop
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module PrivateKey
-
3
module Remote
-
3
class Remove < Action
-
3
attr_accessor :account
-
3
attr_accessor :private_key
-
3
attr_accessor :remote
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::PrivateKey] private_key
-
# @param [AMA::Chef::User::Model::PrivateKey::Remote] remote
-
3
def initialize(account, private_key, remote)
-
1
@account = account
-
1
@private_key = private_key
-
1
@remote = remote
-
end
-
-
3
def apply(resource_factory)
-
account = @account
-
key = @private_key
-
remote = @remote
-
name = "#{account.id}:#{key.owner}:#{key.id}:#{remote.id}"
-
resource_factory.ssh_config name do
-
host remote.id.to_s
-
user account.id.to_s
-
action :remove
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module PrivateKey
-
3
class Remove < Action
-
3
attr_accessor :account
-
3
attr_accessor :key
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::PrivateKey] key
-
3
def initialize(account, key)
-
2
@account = account
-
2
@key = key
-
end
-
-
3
def apply(resource_factory)
-
account = @account
-
key = @key
-
name = "#{account.id}:#{key.owner}:#{key.id}"
-
content = (key.content || '').to_s
-
resource_factory.ssh_private_key name do
-
id key.id.to_s
-
user account.id.to_s
-
# TODO: remove when https://github.com/ama-team/cookbook-ssh-private-keys/issues/2
-
# is resolved
-
content content
-
action :remove
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../action'
-
3
require_relative '../../..//handler/privilege'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module Privilege
-
3
class Grant < Action
-
3
attr_accessor :account
-
3
attr_accessor :privilege
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::Privilege] privilege
-
3
def initialize(account, privilege)
-
4
@account = account
-
4
@privilege = privilege
-
end
-
-
3
def apply(resource_factory)
-
registry = ::AMA::Chef::User::Handler::Privilege
-
handler = registry.retrieve!(:account, @privilege.type)
-
handler.grant(resource_factory, @account, @privilege)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module Privilege
-
3
class Purge < Action
-
3
attr_accessor :account
-
-
# @param [AMA::Chef::User::Model::Account] account
-
3
def initialize(account)
-
26
@account = account
-
end
-
-
3
def apply(*)
-
noop
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module Privilege
-
3
class Revoke < Action
-
3
attr_accessor :account
-
3
attr_accessor :privilege
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::Privilege] type
-
3
def initialize(account, privilege)
-
3
@account = account
-
3
@privilege = privilege
-
end
-
-
3
def apply(resource_factory)
-
registry = ::AMA::Chef::User::Handler::Privilege
-
handler = registry.retrieve!(:account, @privilege.type)
-
handler.revoke(resource_factory, @account, @privilege)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module PublicKey
-
3
class Add < Action
-
3
attr_accessor :account
-
3
attr_accessor :key
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::PublicKey] public_key
-
3
def initialize(account, public_key)
-
16
@account = account
-
16
@key = public_key
-
end
-
-
3
def apply(resource_factory)
-
account = @account
-
key = @key
-
id = "#{account.id}:#{key.owner}:#{key.id}"
-
resource_factory.ssh_authorize_key id do
-
user account.id.to_s
-
key key.content
-
keytype key.type.to_s
-
comment "#{key.owner}:#{key.id}"
-
validate_key key.validate
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../action'
-
3
require_relative '../../../helper/ssh_methods'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module PublicKey
-
3
class Purge < Action
-
3
include Helper::SSHMethods
-
-
3
attr_accessor :account
-
-
# @param [AMA::Chef::User::Model::Account] account
-
3
def initialize(account)
-
18
@account = account
-
end
-
-
3
def apply(resource_factory)
-
resource_id = "#{ssh_directory(@account.id)}/authorized_keys"
-
::Chef::Log.debug("Purging ssh keys for account #{@account.id}")
-
resource_factory.file resource_id do
-
action :delete
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
module PublicKey
-
3
class Remove < Action
-
3
attr_accessor :account
-
3
attr_accessor :key
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::PublicKey] key
-
3
def initialize(account, key)
-
7
@account = account
-
7
@key = key
-
end
-
-
3
def apply(*)
-
noop
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Account
-
3
class Remove < Action
-
3
attr_accessor :account
-
-
# @param [AMA::Chef::User::Model::Account] account
-
3
def initialize(account)
-
10
@account = account
-
end
-
-
3
def apply(resource_factory)
-
resource_factory.user @account.id.to_s do
-
action :remove
-
end
-
end
-
-
3
def to_s
-
"account[#{account.id}]:remove"
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Group
-
3
class AppendMembers < Action
-
3
attr_accessor :group
-
-
# @param [AMA::Chef::User::Model::Group] group
-
3
def initialize(group)
-
5
@group = group
-
end
-
-
3
def apply(resource_factory)
-
group = @group
-
resource_factory.group "#{group.id}:members:append" do
-
group_name group.id.to_s
-
members group.members.to_a
-
append true
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Group
-
3
class ExcludeMembers < Action
-
3
attr_accessor :group
-
3
attr_accessor :members
-
-
# @param [AMA::Chef::User::Model::Group] group
-
# @param [Enumerable] members
-
3
def initialize(group, members)
-
4
@group = group
-
4
@members = members
-
end
-
-
3
def apply(resource_factory)
-
members = @members
-
group = @group
-
resource_factory.group "#{group.id}:members:remove" do
-
group_name group.id.to_s
-
excluded_members members.to_a
-
append true
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../action'
-
3
require_relative '../../../handler/privilege'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Group
-
3
module Privilege
-
3
class Grant < Action
-
3
attr_accessor :group
-
3
attr_accessor :privilege
-
-
# @param [AMA::Chef::User::Model::Group] group
-
# @param [AMA::Chef::User::Model::Privilege] privilege
-
3
def initialize(group, privilege)
-
4
@group = group
-
4
@privilege = privilege
-
end
-
-
3
def apply(resource_factory)
-
registry = ::AMA::Chef::User::Handler::Privilege
-
handler = registry.retrieve!(:group, @privilege.type)
-
handler.grant(resource_factory, @group, @privilege)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Group
-
3
module Privilege
-
3
class Purge < Action
-
3
attr_accessor :group
-
-
# @param [AMA::Chef::User::Model::Group] group
-
3
def initialize(group)
-
15
@group = group
-
end
-
-
3
def apply(*)
-
noop
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../../action'
-
3
require_relative '../../../handler/privilege'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Group
-
3
module Privilege
-
3
class Revoke < Action
-
3
attr_accessor :group
-
3
attr_accessor :privilege
-
-
# @param [AMA::Chef::User::Model::Group] group
-
# @param [AMA::Chef::User::Model::Privilege] privilege
-
3
def initialize(group, privilege)
-
3
@group = group
-
3
@privilege = privilege
-
end
-
-
3
def apply(resource_factory)
-
registry = ::AMA::Chef::User::Handler::Privilege
-
handler = registry.retrieve!(:group, @privilege.type)
-
handler.revoke(resource_factory, @group, @privilege)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Group
-
3
class Remove < Action
-
3
attr_accessor :group
-
-
# @param [AMA::Chef::User::Model::Group] group
-
3
def initialize(group)
-
5
@group = group
-
end
-
-
3
def apply(resource_factory)
-
group = @group
-
resource_factory.group "#{@group.id}:remove" do
-
group_name group.id.to_s
-
action :remove
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../action'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Action
-
3
module Group
-
3
class SetMembers < Action
-
3
attr_accessor :group
-
-
# @param [AMA::Chef::User::Model::Group] group
-
3
def initialize(group)
-
5
@group = group
-
end
-
-
3
def apply(resource_factory)
-
group = @group
-
resource_factory.group "#{group.id}:members:set" do
-
group_name group.id.to_s
-
members group.members.to_a
-
append false
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Exception
-
# Designed to be thrown whenever invalid key is supplied
-
3
class InvalidKeyException < ArgumentError
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Exception
-
# Thrown in case privilege handler isn't found
-
3
class MissingHandlerException < ArgumentError
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
# rubocop:disable Lint/UnusedMethodArgument
-
-
3
require_relative '../exception/missing_handler_exception'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Handler
-
# Privilege handler base / registry
-
3
class Privilege
-
3
def grant(resource_factory, owner, privilege)
-
abstract_method_protector
-
end
-
-
3
def revoke(resource_factory, owner, privilege)
-
abstract_method_protector
-
end
-
-
3
private
-
-
3
def abstract_method_protector
-
raise 'Abstract method hasn\'t been implemented'
-
end
-
-
3
class << self
-
3
def register(domain, type, handler)
-
6
domain = domain.to_sym
-
6
type = type.to_sym
-
6
registry[domain] = {} unless registry[domain]
-
6
registry[domain][type] = handler
-
end
-
-
3
def retrieve(domain, type)
-
domain = domain.to_sym
-
type = type.to_sym
-
registry[domain] && registry[domain][type]
-
end
-
-
3
def retrieve!(domain, type)
-
handler = retrieve(domain, type)
-
return handler if handler
-
message = "No privilege handler with type #{type} " \
-
"has been found in domain #{domain}"
-
raise Exception::MissingHandlerException, message
-
end
-
-
3
private
-
-
3
def registry
-
18
@registry = {} unless @registry
-
18
@registry
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../privilege'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Handler
-
3
class Privilege
-
3
module Account
-
# Sudo privilege granter/revoker
-
3
class Sudo < Privilege
-
3
def grant(resource_factory, owner, privilege)
-
resource(resource_factory, owner, privilege).action = :install
-
end
-
-
3
def revoke(resource_factory, owner, privilege)
-
resource(resource_factory, owner, privilege).action = :remove
-
end
-
-
3
private
-
-
3
def resource(resource_factory, owner, privilege)
-
resource_factory.sudo "user:#{owner.id}" do
-
privilege.options.each do |key, value|
-
if self.class.properties.keys.include?(key)
-
send(key, value)
-
else
-
::Chef::Log.warn(
-
"Account #{owner.id} sudo privilege " \
-
"has unknown option #{key}"
-
)
-
end
-
end
-
user owner.id.to_s
-
end
-
end
-
-
# yes, i'm evading excessive line length
-
3
::AMA::Chef::User::Handler::Privilege.tap do |registry|
-
3
registry.register(:account, :sudo, new)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../privilege'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Handler
-
3
class Privilege
-
3
module Group
-
# Sudo privilege granter/revoker
-
3
class Sudo < Privilege
-
3
def grant(resource_factory, owner, privilege)
-
resource(resource_factory, owner, privilege).action = :install
-
end
-
-
3
def revoke(resource_factory, owner, privilege)
-
resource(resource_factory, owner, privilege).action = :remove
-
end
-
-
3
private
-
-
3
def resource(resource_factory, owner, privilege)
-
resource_factory.sudo "group:#{owner.id}" do
-
privilege.options.each do |key, value|
-
if self.class.properties.keys.include?(key)
-
send(key, value)
-
else
-
::Chef::Log.warn(
-
"Account #{owner.id} sudo privilege " \
-
"has unknown option #{key}"
-
)
-
end
-
end
-
group owner.id.to_s
-
end
-
end
-
-
# yes, i'm evading excessive line length
-
3
::AMA::Chef::User::Handler::Privilege.tap do |registry|
-
3
registry.register(:group, :sudo, new)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Helper
-
# Handful of methods that may be helpful for SSH file-related work
-
3
module SSHMethods
-
3
def home_directory(account_id)
-
Etc.getpwnam(account_id.to_s).dir
-
rescue ArgumentError
-
"/home/#{account_id}"
-
end
-
-
3
def ssh_directory(account_id)
-
"#{home_directory(account_id)}/.ssh"
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require 'ama-entity-mapper'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Mixin
-
# Base methods for entities (models) used across project
-
3
module Entity
-
3
class << self
-
3
def included(klass)
-
39
klass.send(:include, AMA::Entity::Mapper::DSL)
-
end
-
end
-
-
3
def ==(other)
-
635
eql?(other)
-
end
-
-
3
def eql?(other)
-
635
return false unless other.is_a?(self.class) || is_a?(other.class)
-
272
return false unless other.instance_variables == instance_variables
-
272
instance_variables.each do |variable|
-
728
value = other.instance_variable_get(variable)
-
728
return false unless instance_variable_get(variable) == value
-
end
-
147
true
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../mixin/entity'
-
3
require_relative 'policy'
-
3
require_relative 'public_key'
-
3
require_relative 'private_key'
-
3
require_relative 'privilege'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
# Depicts linux user account
-
#
-
# Private and public keys are stored in { owner => { owner keys } }
-
# structure (grouped by effective owner).
-
#
-
# @!attribute id
-
# @return [Symbol]
-
# @!attribute privileges
-
# @return [Hash{Symbol, Privilege}]
-
# @!attribute public_keys
-
# @return [Hash{Symbol, Hash{Symbol, PublicKey}}]
-
# @!attribute private_keys
-
# @return [Hash{Symbol, Hash{Symbol, PrivateKey}]
-
# @!attribute policy
-
# @return [Symbol] :edit or :manage
-
3
class Account
-
3
include Mixin::Entity
-
-
# rubocop:disable Metrics/LineLength
-
3
attribute :id, Symbol
-
3
attribute :privileges, [Hash, K: Symbol, V: Privilege], default: {}
-
3
attribute :public_keys, [Hash, K: Symbol, V: [Hash, K: Symbol, V: PublicKey]], default: {}
-
3
attribute :private_keys, [Hash, K: Symbol, V: [Hash, K: Symbol, V: PrivateKey]], default: {}
-
3
attribute :policy, Policy, default: Policy::NONE
-
# rubocop:enable Metrics/LineLength
-
-
3
denormalizer_block do |input, type, context, &block|
-
76
if input.is_a?(Hash) && [:id, 'id'].none? { |key| input.key?(key) }
-
24
input[:id] = context.path.current.name
-
end
-
26
block.call(input, type, context)
-
end
-
-
3
def initialize(id = nil)
-
108
@id = id
-
108
@privileges = {}
-
108
@public_keys = {}
-
108
@private_keys = {}
-
108
@policy = Policy::NONE
-
end
-
-
3
def policy=(policy)
-
163
@policy = Policy.wrap(policy)
-
end
-
-
3
def public_keys!(client_id)
-
24
public_keys[client_id] = {} unless public_keys.key?(client_id)
-
24
public_keys[client_id]
-
end
-
-
3
def private_keys!(client_id)
-
2
private_keys[client_id] = {} unless private_keys.key?(client_id)
-
2
private_keys[client_id]
-
end
-
-
# rubocop:disable Metrics/AbcSize
-
# @param [Account] other
-
3
def merge(other)
-
18
other.privileges.each do |type, privilege|
-
2
privileges[type] = privilege unless privileges[type]
-
2
privileges[type].options.merge!(privilege.options)
-
end
-
18
other.public_keys.each do |owner, keys|
-
12
public_keys!(owner).merge!(keys)
-
end
-
18
other.private_keys.each do |owner, keys|
-
1
private_keys!(owner).merge!(keys)
-
end
-
18
self.policy = [policy, other.policy].max
-
18
self
-
end
-
# rubocop:enable Metrics/AbcSize
-
-
3
def to_s
-
"Account :#{id} { policy: :#{policy} }"
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../mixin/entity'
-
3
require_relative 'public_key'
-
3
require_relative 'private_key'
-
3
require_relative 'client/role_tree'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
# Represents external node client
-
3
class Client
-
3
include Mixin::Entity
-
-
3
attribute :id, Symbol
-
3
attribute :roles, RoleTree, default: RoleTree.new
-
3
attribute :public_keys, [Hash, K: Symbol, V: PublicKey]
-
3
attribute :private_keys, [Hash, K: Symbol, V: PrivateKey]
-
-
3
def initialize(id = nil)
-
136
@id = id
-
end
-
-
3
denormalizer_block do |input, type, context, &block|
-
66
input = {} if input.nil?
-
198
if input.is_a?(Hash) && ['id', :id].none? { |key| input.key?(key) }
-
66
input[:id] = context.path.current.name
-
end
-
66
block.call(input, type, context)
-
end
-
-
3
def to_s
-
"Client :#{id}"
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../mixin/entity'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
3
class Client
-
# Internal structure for client roles
-
3
class RoleTree
-
3
include Mixin::Entity
-
-
3
attribute :root, [Hash, K: Symbol, V: [:*, NilClass]], default: {}
-
-
3
normalizer_block do |entity, *|
-
12
entity.root
-
end
-
-
3
denormalizer_block do |input, *|
-
66
break RoleTree.new(input) if input.is_a?(Hash)
-
raise "Expected Hash, received #{input.class}"
-
end
-
-
3
def initialize(root = {})
-
139
@root = root
-
end
-
-
3
def contains(path)
-
48
cursor = @root
-
48
path.each do |segment|
-
58
return false unless cursor.respond_to?(:key?)
-
58
candidates = [segment.to_s, segment.to_sym]
-
58
next_segment = candidates.find do |candidate|
-
113
cursor.key?(candidate)
-
end
-
58
return false unless next_segment
-
30
cursor = cursor[next_segment]
-
end
-
20
true
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require 'set'
-
-
3
require_relative '../mixin/entity'
-
3
require_relative 'policy'
-
3
require_relative 'privilege'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
# Represents Linux user group
-
3
class Group
-
3
include Mixin::Entity
-
-
3
attribute :id, Symbol
-
3
attribute :members, [Set, T: Symbol], default: Set.new
-
3
attribute :privileges, [Hash, K: Symbol, V: Privilege], default: {}
-
3
attribute :policy, Policy, default: Policy::NONE
-
-
3
denormalizer_block do |input, type, context, &block|
-
28
if input.is_a?(Hash) && [:id, 'id'].none? { |key| input.key?(key) }
-
8
input[:id] = context.path.current.name
-
end
-
10
block.call(input, type, context)
-
end
-
-
3
def initialize(id = nil)
-
41
@id = id
-
41
@members = Set.new
-
41
@privileges = {}
-
41
@policy = Policy::NONE
-
end
-
-
3
def policy=(policy)
-
57
@policy = Policy.wrap(policy)
-
end
-
-
# @param [Group] other
-
3
def merge(other)
-
raise 'Different ids' unless id == other.id
-
members.merge(other.members)
-
privileges.merge!(other.privileges)
-
self.policy = [policy, other.policy].max
-
self
-
end
-
-
3
def to_s
-
"Group :#{id}"
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require 'ama-entity-mapper'
-
-
3
require_relative 'privilege'
-
3
require_relative 'partition/filter'
-
3
require_relative 'partition/policy_group'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
# Represents definition of client partition: how clients will be matched
-
# and what would be done next
-
3
class Partition
-
3
include Mixin::Entity
-
-
3
attribute :id, Symbol
-
3
attribute :privileges, [Hash, K: Symbol, V: Privilege], default: {}
-
3
attribute :filters, [Enumerable, T: Filter], default: []
-
3
attribute :policy, PolicyGroup, default: PolicyGroup::DEFAULT
-
3
attribute :impersonation, [Hash, K: Symbol, V: [:*, NilClass]]
-
-
3
denormalizer_block do |input, type, context, &block|
-
87
input = [context.path.current.name] if input.nil?
-
87
if input.is_a?(Enumerable) && !input.is_a?(Hash)
-
11
input = { filters: input }
-
end
-
261
if input.is_a?(Hash) && ['id', :id].none? { |key| input.key?(key) }
-
87
input[:id] = context.path.current.name
-
end
-
87
block.call(input, type, context)
-
end
-
-
# @param [AMA::Chef::User::Model::Client] client
-
3
def applies_to(client)
-
68
filters.any? { |filter| filter.apply(client) }
-
end
-
-
3
def to_s
-
"Partition #{id}"
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../mixin/entity'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
3
class Partition
-
# Role filter for matching clients that fit into partition
-
3
class Filter
-
3
include Mixin::Entity
-
-
3
attribute :roles, [Enumerable, T: [Enumerable, T: String]]
-
-
3
normalizer_block do |entity, *|
-
entity.roles.map do |path|
-
15
path.join('.')
-
14
end .join('+')
-
end
-
-
3
denormalizer_block do |input, *|
-
72
break input if input.is_a?(Filter.class)
-
72
unless input.is_a?(String)
-
raise "Expected String, got #{input.class}"
-
end
-
72
Filter.new.tap do |instance|
-
72
instance.roles = input.split('+').map do |role|
-
75
role.split('.').map(&:strip).map(&:to_sym).reject(&:empty?)
-
end
-
end
-
end
-
-
3
def initialize(roles = [])
-
144
self.roles = roles
-
end
-
-
# @param [AMA::Chef::User::Model::Client] account
-
3
def apply(account)
-
45
roles.all? do |role|
-
48
account.roles.contains(role)
-
end
-
end
-
-
3
def to_s
-
"Partition.Filter `#{normalize}`"
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../mixin/entity'
-
3
require_relative '../policy'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
3
class Partition
-
# Determines how user and group accounts will be treated
-
3
class PolicyGroup
-
3
include Mixin::Entity
-
-
3
attribute :account, Policy, default: Policy::EDIT
-
3
attribute :group, Policy, default: Policy::EDIT
-
-
3
DEFAULT = new.tap do |instance|
-
3
instance.account = Policy::EDIT
-
3
instance.group = Policy::EDIT
-
end
-
-
3
def to_s
-
"Partition.Policy {account: #{account}, group: #{group}}"
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../mixin/entity'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
# Helper class for mangling entity policies
-
3
class Policy
-
3
include Comparable
-
3
include Mixin::Entity
-
-
3
def self.values
-
22
%i[none edit manage]
-
end
-
-
3
attribute :value, Symbol, values: values, default: :none
-
-
3
normalizer_block do |entity, *|
-
359
entity.value
-
end
-
-
3
denormalizer_block do |input, type, context, &block|
-
210
if input.is_a?(String) || input.is_a?(Symbol)
-
210
input = { value: input }
-
end
-
210
block.call(input, type, context)
-
end
-
-
3
def initialize(value = :none)
-
327
self.value = value
-
end
-
-
3
NONE = new(:none)
-
3
EDIT = new(:edit)
-
3
MANAGE = new(:manage)
-
-
3
def self.wrap(value)
-
239
return value if value.is_a?(Policy)
-
60
condition = value.is_a?(String) || value.is_a?(Symbol)
-
60
if condition && Policy.const_defined?(value.upcase)
-
60
return Policy.const_get(value.upcase)
-
end
-
raise ArgumentError, "Invalid policy value: #{value}"
-
end
-
-
3
def edit?
-
self > NONE
-
end
-
-
3
def create?
-
self > NONE
-
end
-
-
3
def remove?
-
80
self == MANAGE
-
end
-
-
3
def manage?
-
self == MANAGE
-
end
-
-
3
def nothing?
-
14
self == NONE
-
end
-
-
3
def <=>(other)
-
19
other = Policy.wrap(other)
-
19
unless other.is_a?(Policy)
-
raise ArgumentError, "Invalid input: #{other.inspect}"
-
end
-
19
values = self.class.values
-
19
values.index(value) - values.index(other.value)
-
end
-
-
3
def eq?(other)
-
583
return false unless other.is_a?(Policy)
-
583
other.value == @value
-
end
-
-
3
def ==(other)
-
583
eq?(other)
-
end
-
-
3
def to_s
-
value.to_s
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require 'digest'
-
-
3
require_relative '../mixin/entity'
-
3
require_relative 'private_key/remote'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
3
class PrivateKey
-
3
include Mixin::Entity
-
-
3
attribute :id, Symbol
-
3
attribute :owner, Symbol
-
3
attribute :type, Symbol, nullable: true
-
3
attribute :content, String, sensitive: true, nullable: true
-
3
attribute :digest, String
-
3
attribute :public_key, String, sensitive: true, nullable: true
-
3
attribute :passphrase, String, sensitive: true, nullable: true
-
3
attribute :remotes, [Hash, K: Symbol, V: Remote], default: {}
-
3
attribute :validate, TrueClass, FalseClass, default: true
-
3
attribute :install_public_key, TrueClass, FalseClass, default: false
-
-
3
denormalizer_block do |input, type, context, &block|
-
17
input = { content: input } if input.is_a?(String)
-
17
raise "Hash expected, got #{input.class}" unless input.is_a?(Hash)
-
17
{ id: -1, owner: -3 }.each do |key, index|
-
102
if [key, key.to_s].none? { |v| input.key?(v) }
-
32
segment = context.path.segments[index]
-
32
input[key] = segment.name unless segment.nil?
-
end
-
end
-
17
block.call(input, type, context)
-
end
-
-
3
normalizer_block do |entity, type, context, &block|
-
22
normalized = block.call(entity, type, context)
-
22
if context.include_sensitive_attributes
-
normalized[:content] = entity.content
-
end
-
22
normalized
-
end
-
-
3
def content=(content)
-
37
return @content = @digest = nil if content.nil?
-
37
@digest = Digest::MD5.hexdigest(content)
-
37
@content = content
-
end
-
-
3
def ==(other)
-
26
return false unless other.is_a?(self.class)
-
9
@id == other.id && @owner == other.owner && @digest == other.digest
-
end
-
-
3
def merge(other)
-
@public_key = other.public_key if other.public_key
-
@remotes.merge!(other.remotes)
-
@validate = true if other.validate
-
@install_public_key = true if other.install_public_key
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../../mixin/entity'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
3
class PrivateKey
-
3
class Remote
-
3
include Mixin::Entity
-
-
3
attribute :id, Symbol
-
3
attribute :options, [Hash, K: String, V: [String, Integer]]
-
-
3
denormalizer_block do |input, type, context, &block|
-
8
input = {} if input.nil?
-
8
if input.is_a?(String) || input.is_a?(Symbol)
-
4
input = { User: input }
-
end
-
8
raise "Expected Hash, got #{input.class}" unless input.is_a?(Hash)
-
8
target = {
-
options: input[:options] || input['options'] || {},
-
id: context.path.current.name
-
}
-
8
['id', :id].each do |key|
-
16
target[:id] = input[key] if input[key]
-
end
-
8
input.each do |key, value|
-
6
next if %i[id options].include?(key.to_sym)
-
6
target[:options][key] = value
-
end
-
8
block.call(target, type, context)
-
end
-
-
3
def initialize(id = nil)
-
19
@id = id
-
19
@options = {}
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../mixin/entity'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
3
class Privilege
-
3
include Mixin::Entity
-
-
3
attribute :type, Symbol
-
3
attribute :options, [Hash, K: Symbol, V: [:*, NilClass]], default: {}
-
-
3
denormalizer_block do |input, type, context, &block|
-
36
input = {} unless input.is_a?(Hash)
-
36
target = {}
-
36
keys = ['type', 'options', :type, :options]
-
36
keys.each do |key|
-
144
target[key.to_sym] = input[key] if input[key]
-
end
-
36
target[:options] = {} unless target[:options].is_a?(Hash)
-
36
input.each do |key, value|
-
6
next if keys.include?(key)
-
6
target[:options][key] = value
-
end
-
36
target[:type] = context.path.current.name unless target.key?(:type)
-
36
block.call(target, type, context)
-
end
-
-
3
def to_s
-
"Privilege :#{type}"
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require 'digest'
-
-
3
require_relative '../mixin/entity'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
3
class PublicKey
-
3
include Mixin::Entity
-
-
3
attribute :owner, Symbol
-
3
attribute :id, Symbol
-
3
attribute :type, Symbol, default: :'ssh-rsa'
-
3
attribute :content, String, sensitive: true, nullable: true
-
3
attribute :digest, String
-
3
attribute :validate, TrueClass, FalseClass, default: true
-
-
3
denormalizer_block do |input, type, context, &block|
-
47
input = { content: input } if input.is_a?(String)
-
47
raise "Hash expected, got #{input.class}" unless input.is_a?(Hash)
-
47
{ id: -1, owner: -3 }.each do |key, index|
-
282
if [key, key.to_s].none? { |v| input.key?(v) }
-
58
segment = context.path.segments[index]
-
58
input[key] = segment.name unless segment.nil?
-
end
-
end
-
47
block.call(input, type, context)
-
end
-
-
3
normalizer_block do |entity, type, context, &block|
-
156
normalized = block.call(entity, type, context)
-
156
if context.include_sensitive_attributes
-
1
normalized[:content] = entity.content
-
end
-
156
normalized
-
end
-
-
3
def full_id
-
raise 'Missing owner' unless @owner
-
raise 'Missing id' unless @id
-
"#{@owner}:#{@id}"
-
end
-
-
3
def content=(content)
-
101
@digest = content.nil? ? nil : Digest::MD5.hexdigest(content)
-
101
@content = content
-
end
-
-
3
def to_s
-
"PublicKey #{owner}:#{id}"
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative '../mixin/entity'
-
-
3
require_relative 'account'
-
3
require_relative 'group'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module Model
-
# Represents complete node state
-
3
class State
-
3
include Mixin::Entity
-
-
# !@attribute groups
-
# @return [Hash{Symbol, Group}]
-
3
attribute :groups, [Hash, K: Symbol, V: Group], default: {}
-
# !@attribute accounts
-
# @return [Hash{Symbol, Account}]
-
3
attribute :accounts, [Hash, K: Symbol, V: Account], default: {}
-
# !@attribute version
-
# @return [Integer]
-
3
attribute :version, Integer, default: 1
-
-
3
def initialize
-
92
@groups = {}
-
92
@accounts = {}
-
92
@version = 1
-
end
-
-
# @return [AMA::Chef::User::Model::Account]
-
3
def account!(id)
-
18
id = id.to_sym
-
18
unless accounts[id]
-
15
accounts[id] = Account.new
-
15
accounts[id].id = id
-
15
accounts[id].policy = Policy::NONE
-
end
-
18
account(id)
-
end
-
-
# @return [AMA::Chef::User::Model::Account]
-
3
def account(id)
-
18
accounts[id.to_sym]
-
end
-
-
# @return [AMA::Chef::User::Model::Group]
-
3
def group!(id)
-
id = id.to_sym
-
unless groups[id]
-
groups[id] = Group.new
-
groups[id].id = id
-
end
-
group(id)
-
end
-
-
# @return [AMA::Chef::User::Model::Group]
-
3
def group(id)
-
5
groups[id.to_sym]
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require 'set'
-
-
3
::Dir.glob("#{__dir__}/action/**/*.rb").each do |path|
-
63
require path
-
end
-
-
3
require_relative 'planner/account'
-
3
require_relative 'planner/group'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
# This class is responsible for creating list of actions required for
-
# converging from current state to target state
-
3
class Planner
-
3
def initialize
-
10
@accounts = Account.new
-
10
@groups = Group.new
-
end
-
-
# @param [AMA::Chef::User::Model::State] current_state
-
# @param [AMA::Chef::User::Model::State] desired_state
-
3
def plan(current_state, desired_state)
-
18
plan = [
-
*@accounts.plan(current_state.accounts, desired_state.accounts),
-
*@groups.plan(current_state.groups, desired_state.groups)
-
]
-
18
plan.each do |action|
-
119
action.class_name = action.class.to_s
-
end
-
18
plan
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative 'account/public_key'
-
3
require_relative 'account/private_key'
-
3
require_relative 'account/privilege'
-
3
require_relative '../action/account/create'
-
3
require_relative '../action/account/remove'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Planner
-
# This planner creates actions altering account state
-
3
class Account
-
3
def initialize
-
16
@public_keys = PublicKey.new
-
16
@private_keys = PrivateKey.new
-
16
@privileges = Privilege.new
-
end
-
-
# @param [Hash{Symbol, AMA::Chef::User::Model::Account}] current_state
-
# @param [Hash{Symbol, AMA::Chef::User::Model::Account}] desired_state
-
3
def plan(current_state, desired_state)
-
24
(current_state.keys | desired_state.keys).flat_map do |id|
-
32
process(current_state[id], desired_state[id])
-
end
-
end
-
-
# @param [AMA::Chef::User::Model::Account] current_state
-
# @param [AMA::Chef::User::Model::Account] desired_state
-
3
def process(current_state, desired_state)
-
32
actions = [
-
*process_public_keys(current_state, desired_state),
-
*process_private_keys(current_state, desired_state),
-
*process_privileges(current_state, desired_state)
-
]
-
32
if !desired_state.nil?
-
16
actions.unshift(ns::Create.new(desired_state))
-
16
elsif current_state.policy.remove?
-
10
actions.push(ns::Remove.new(current_state))
-
end
-
32
actions
-
end
-
-
3
private
-
-
3
def ns
-
26
::AMA::Chef::User::Action::Account
-
end
-
-
# @param [AMA::Chef::User::Model::Account] current_state
-
# @param [AMA::Chef::User::Model::Account] desired_state
-
3
def process_public_keys(current_state, desired_state)
-
32
return [] if desired_state.nil? && !current_state.policy.remove?
-
26
account = desired_state || current_state
-
26
current_keys = current_state ? current_state.public_keys : {}
-
26
desired_keys = desired_state ? desired_state.public_keys : {}
-
26
@public_keys.plan(account, current_keys, desired_keys)
-
end
-
-
# @param [AMA::Chef::User::Model::Account] current_state
-
# @param [AMA::Chef::User::Model::Account] desired_state
-
3
def process_private_keys(current_state, desired_state)
-
32
return [] if desired_state.nil? && !current_state.policy.remove?
-
26
account = desired_state || current_state
-
26
current_keys = current_state ? current_state.private_keys : {}
-
26
desired_keys = desired_state ? desired_state.private_keys : {}
-
26
@private_keys.plan(account, current_keys, desired_keys)
-
end
-
-
# @param [AMA::Chef::User::Model::Account] current_state
-
# @param [AMA::Chef::User::Model::Account] desired_state
-
3
def process_privileges(current_state, desired_state)
-
32
return [] if desired_state.nil? && !current_state.policy.remove?
-
26
account = desired_state || current_state
-
26
current_privileges = current_state ? current_state.privileges : {}
-
26
desired_privileges = desired_state ? desired_state.privileges : {}
-
26
@privileges.plan(account, current_privileges, desired_privileges)
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
# rubocop:disable Metrics/LineLength
-
-
3
require_relative 'private_key/remote'
-
3
require_relative '../../action/account/private_key/add'
-
3
require_relative '../../action/account/private_key/remove'
-
3
require_relative '../../action/account/private_key/purge'
-
3
require_relative '../../action/account/public_key/add'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Planner
-
3
class Account
-
# This planner creates actions altering account private keys
-
3
class PrivateKey
-
3
def initialize
-
20
@remote = Remote.new
-
end
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [Hash{Symbol, Hash{Symbol, AMA::Chef::User::Model::PrivateKey}}] current_state
-
# @param [Hash{Symbol, Hash{Symbol, AMA::Chef::User::Model::PrivateKey}}] desired_state
-
3
def plan(account, current_state, desired_state)
-
30
owners = current_state.keys | desired_state.keys
-
30
actions = owners.flat_map do |owner|
-
5
current_keys = current_state[owner] || {}
-
5
desired_keys = desired_state[owner] || {}
-
5
(current_keys.keys | desired_keys.keys).flat_map do |key|
-
5
process(account, current_keys[key], desired_keys[key])
-
end
-
end
-
30
actions.push(ns::Purge.new(account)) if desired_state.empty?
-
30
actions
-
end
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::PrivateKey] current_state
-
# @param [AMA::Chef::User::Model::PrivateKey] desired_state
-
3
def process(account, current_state, desired_state)
-
5
current_remotes = current_state ? current_state.remotes : {}
-
5
desired_remotes = desired_state ? desired_state.remotes : {}
-
5
key = desired_state || current_state
-
5
actions = @remote.plan(
-
account,
-
key,
-
current_remotes,
-
desired_remotes
-
)
-
5
if desired_state.nil?
-
2
actions.push(ns::Remove.new(account, current_state))
-
else
-
3
actions.unshift(ns::Add.new(account, desired_state))
-
end
-
5
actions
-
end
-
-
3
def ns
-
32
::AMA::Chef::User::Action::Account::PrivateKey
-
end
-
-
3
def pubkey_ns
-
::AMA::Chef::User::Action::Account::PublicKey
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
# rubocop:disable Metrics/LineLength
-
-
3
require_relative '../../../action/account/private_key/remote/add'
-
3
require_relative '../../../action/account/private_key/remote/remove'
-
3
require_relative '../../../action/account/private_key/remote/purge'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Planner
-
3
class Account
-
3
class PrivateKey
-
# This planner creates actions altering private key remotes
-
3
class Remote
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::PrivateKey] private_key
-
# @param [Hash{Symbol, AMA::Chef::User::Model::PrivateKey::Remote}] current_state
-
# @param [Hash{Symbol, AMA::Chef::User::Model::PrivateKey::Remote}] desired_state
-
3
def plan(account, private_key, current_state, desired_state)
-
8
current_state ||= {}
-
8
desired_state ||= {}
-
8
actions = (current_state.keys | desired_state.keys).map do |key|
-
3
current = current_state[key]
-
3
desired = desired_state[key]
-
3
process(account, private_key, current, desired)
-
end
-
8
if desired_state.empty?
-
6
actions.push(ns::Purge.new(account, private_key))
-
end
-
8
actions
-
end
-
-
3
private
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::PrivateKey] private_key
-
# @param [AMA::Chef::User::Model::PrivateKey::Remote] current_state
-
# @param [AMA::Chef::User::Model::PrivateKey::Remote] desired_state
-
3
def process(account, private_key, current_state, desired_state)
-
3
if desired_state.nil?
-
1
ns::Remove.new(account, private_key, current_state)
-
else
-
2
ns::Add.new(account, private_key, desired_state)
-
end
-
end
-
-
3
def ns
-
9
::AMA::Chef::User::Action::Account::PrivateKey::Remote
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
# rubocop:disable Metrics/LineLength
-
-
3
require_relative '../../action/account/privilege/grant'
-
3
require_relative '../../action/account/privilege/revoke'
-
3
require_relative '../../action/account/privilege/purge'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Planner
-
3
class Account
-
# This planner creates actions altering account state
-
3
class Privilege
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [Hash{Symbol, AMA::Chef::User::Model::Privilege}] current_state
-
# @param [Hash{Symbol, AMA::Chef::User::Model::Privilege}] desired_state
-
3
def plan(account, current_state, desired_state)
-
30
actions = (current_state.keys | desired_state.keys).map do |key|
-
7
process(account, current_state[key], desired_state[key])
-
end
-
30
actions.push(ns::Purge.new(account)) if desired_state.empty?
-
30
actions
-
end
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::Privilege] current_state
-
# @param [AMA::Chef::User::Model::Privilege] desired_state
-
3
def process(account, current_state, desired_state)
-
7
if desired_state.nil?
-
3
ns::Revoke.new(account, current_state)
-
else
-
4
ns::Grant.new(account, desired_state)
-
end
-
end
-
-
3
def ns
-
33
::AMA::Chef::User::Action::Account::Privilege
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
# rubocop:disable Metrics/LineLength
-
-
3
require_relative '../../action/account/public_key/add'
-
3
require_relative '../../action/account/public_key/remove'
-
3
require_relative '../../action/account/public_key/purge'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Planner
-
3
class Account
-
# This planner creates actions altering account privileges
-
3
class PublicKey
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [Hash{Symbol, Hash{Symbol, AMA::Chef::User::Model::PublicKey}}] current_state
-
# @param [Hash{Symbol, Hash{Symbol, AMA::Chef::User::Model::PublicKey}}] desired_state
-
3
def plan(account, current_state, desired_state)
-
31
owners = current_state.keys | desired_state.keys
-
31
actions = owners.flat_map do |owner|
-
21
current_keys = current_state[owner] || {}
-
21
desired_keys = desired_state[owner] || {}
-
21
(current_keys.keys | desired_keys.keys).map do |key|
-
23
process(account, current_keys[key], desired_keys[key])
-
end
-
end
-
31
actions.push(ns::Purge.new(account)) if desired_state.empty?
-
31
actions
-
end
-
-
# @param [AMA::Chef::User::Model::Account] account
-
# @param [AMA::Chef::User::Model::PublicKey] current_state
-
# @param [AMA::Chef::User::Model::PublicKey] desired_state
-
3
def process(account, current_state, desired_state)
-
23
if desired_state.nil?
-
7
ns::Remove.new(account, current_state)
-
else
-
16
ns::Add.new(account, desired_state)
-
end
-
end
-
-
3
def ns
-
41
::AMA::Chef::User::Action::Account::PublicKey
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require_relative 'group/privilege'
-
3
require_relative '../action/group/set_members'
-
3
require_relative '../action/group/append_members'
-
3
require_relative '../action/group/exclude_members'
-
3
require_relative '../action/group/remove'
-
3
require_relative '../model/policy'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Planner
-
# This planner creates actions altering group state
-
3
class Group
-
3
def initialize
-
19
@privilege = Privilege.new
-
end
-
-
# @param [Hash{Symbol, AMA::Chef::User::Model::Group}] current_state
-
# @param [Hash{Symbol, AMA::Chef::User::Model::Group}] desired_state
-
3
def plan(current_state, desired_state)
-
27
(current_state.keys | desired_state.keys).flat_map do |id|
-
18
process(current_state[id], desired_state[id])
-
end
-
end
-
-
3
private
-
-
# @param [AMA::Chef::User::Model::Group] current_state
-
# @param [AMA::Chef::User::Model::Group] desired_state
-
3
def process(current_state, desired_state)
-
18
actions = privilege_actions(current_state, desired_state)
-
18
group = desired_state || current_state
-
18
return [] if group.policy == Model::Policy::NONE
-
18
if desired_state.nil?
-
8
actions.push(*deletion_actions(current_state))
-
else
-
10
actions.push(*creation_actions(current_state, desired_state))
-
end
-
18
post_process_actions(actions)
-
end
-
-
3
def ns
-
19
::AMA::Chef::User::Action::Group
-
end
-
-
3
def creation_actions(current_state, desired_state)
-
10
group = desired_state
-
10
unless desired_state.policy == Model::Policy::EDIT
-
5
return [ns::SetMembers.new(group)]
-
end
-
5
actions = [ns::AppendMembers.new(group)]
-
5
current_members = current_state ? current_state.members : Set.new
-
5
excluded_members = current_members - desired_state.members
-
5
return actions if excluded_members.empty?
-
1
actions.unshift(ns::ExcludeMembers.new(group, excluded_members))
-
end
-
-
3
def deletion_actions(current_state)
-
8
if current_state.policy.remove?
-
5
return [ns::Remove.new(current_state)]
-
end
-
3
return [] if current_state.members.empty?
-
3
[ns::ExcludeMembers.new(current_state, current_state.members)]
-
end
-
-
3
def post_process_actions(actions)
-
18
actions.each do |action|
-
36
action.class_name = action.class.to_s
-
end
-
18
actions
-
end
-
-
3
def privilege_actions(current_state, desired_state)
-
18
return [] if desired_state.nil? && !current_state.policy.remove?
-
15
group = desired_state || current_state
-
15
current = current_state.nil? ? {} : current_state.privileges
-
15
desired = desired_state.nil? ? {} : desired_state.privileges
-
15
@privilege.plan(group, current, desired)
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
# rubocop:disable Metrics/LineLength
-
-
3
require_relative '../../action/group/privilege/grant'
-
3
require_relative '../../action/group/privilege/revoke'
-
3
require_relative '../../action/group/privilege/purge'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
class Planner
-
3
class Group
-
# Planner for group privileges
-
3
class Privilege
-
# @param [AMA::Chef::User::Model::Group] group
-
# @param [Hash{Symbol, AMA::Chef::User::Model::Privilege}] current_state
-
# @param [Hash{Symbol, AMA::Chef::User::Model::Privilege}] desired_state
-
3
def plan(group, current_state, desired_state)
-
19
actions = (current_state.keys | desired_state.keys).map do |type|
-
7
process(group, current_state[type], desired_state[type])
-
end
-
19
actions.push(ns::Purge.new(group)) if desired_state.empty?
-
19
actions
-
end
-
-
3
private
-
-
# @param [AMA::Chef::User::Model::Group] group
-
# @param [AMA::Chef::User::Model::Privilege] current_state
-
# @param [AMA::Chef::User::Model::Privilege] desired_state
-
3
def process(group, current_state, desired_state)
-
7
return ns::Revoke.new(group, current_state) if desired_state.nil?
-
4
ns::Grant.new(group, desired_state)
-
end
-
-
3
def ns
-
22
::AMA::Chef::User::Action::Group::Privilege
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require 'set'
-
-
3
require_relative '../model/policy'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module State
-
# Simple class that builds target state by applying partitions to
-
# clients
-
3
class Builder
-
# @param [Hash{Symbol, AMA::Chef::User::Model::Client}] clients
-
# @param [Hash{Symbol, AMA::Chef::User::Model::Partition}] partitions
-
# @return [AMA::Chef::User::Model::State]
-
3
def build(clients, partitions)
-
9
state = Model::State.new
-
9
partitions.values.each do |partition|
-
13
process_partition(state, partition, clients)
-
end
-
9
clean_state(state)
-
9
state
-
end
-
-
3
private
-
-
# @param [AMA::Chef::User::Model::State] state
-
# @param [AMA::Chef::User::Model::Partition] partition
-
# @param [Hash<Symbol, AMA::Chef::User::Model::Client>] clients
-
3
def process_partition(state, partition, clients)
-
13
unless partition.policy.group == Model::Policy::NONE
-
5
state.groups[partition.id] = extract_group(partition)
-
end
-
13
clients.values.each do |client|
-
30
next unless partition.applies_to(client)
-
14
apply_partition(state, client, partition)
-
end
-
end
-
-
# @param [AMA::Chef::User::Model::State] state
-
# @param [AMA::Chef::User::Model::Client] client
-
# @param [AMA::Chef::User::Model::Partition] partition
-
3
def apply_partition(state, client, partition)
-
14
account = extract_account(client, partition)
-
14
state.account!(client.id).merge(account)
-
14
apply_impersonation(state, client, partition)
-
14
return unless partition.policy.group != Model::Policy::NONE
-
5
state.group(partition.id).members.add(account.id)
-
end
-
-
# @param [AMA::Chef::User::Model::State] state
-
3
def clean_state(state)
-
9
state.accounts.reject! do |_, account|
-
15
account.policy == Model::Policy::NONE
-
end
-
9
state.groups.reject! do |_, group|
-
5
group.policy == Model::Policy::NONE
-
end
-
9
state
-
end
-
-
3
def extract_group(partition)
-
5
group = Model::Group.new(partition.id)
-
5
group.policy = partition.policy.group
-
5
group.privileges = partition.privileges.clone
-
5
group
-
end
-
-
# rubocop:disable Metrics/AbcSize
-
3
def extract_account(client, partition)
-
14
account = Model::Account.new(client.id)
-
14
account.policy = partition.policy.account
-
14
unless client.public_keys.empty?
-
8
account.public_keys!(client.id).merge!(client.public_keys)
-
end
-
14
unless client.private_keys.empty?
-
1
account.private_keys!(client.id).merge!(client.private_keys)
-
end
-
14
if partition.policy.group.nothing? && !partition.privileges.empty?
-
2
account.privileges = partition.privileges.clone
-
end
-
14
account
-
end
-
# rubocop:enable Metrics/AbcSize
-
-
3
def apply_impersonation(state, client, partition)
-
14
return if client.public_keys.empty?
-
8
partition.impersonation.keys.each do |hijacked|
-
4
package = Model::Account.new(hijacked)
-
4
package.policy = :edit
-
4
package.public_keys!(client.id).merge!(client.public_keys)
-
4
state.account!(hijacked).merge(package)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
3
require 'ama-entity-mapper'
-
-
3
require_relative '../model/account'
-
3
require_relative '../model/group'
-
3
require_relative '../model/state'
-
-
3
module AMA
-
3
module Chef
-
3
module User
-
3
module State
-
# Saves data as node attributes
-
3
class Persister
-
3
attr_reader :node
-
-
# @param [Chef::Node] node
-
3
def initialize(node)
-
1
@node = node
-
end
-
-
3
def retrieve(context_name)
-
data = fetch(context_name) || {}
-
Entity::Mapper.map(data, AMA::Chef::User::Model::State)
-
end
-
-
3
def persist(context_name, state)
-
1
save([context_name], Entity::Mapper.normalize(state))
-
end
-
-
3
private
-
-
3
def save(path, data)
-
1
data = data.normalize if data.respond_to? :normalize
-
1
write_handle(*path[0..-2])[path[-1]] = data
-
end
-
-
3
def fetch(*path)
-
read_handle(*path)
-
end
-
-
3
def delete(*path)
-
return if path.empty?
-
path = [path] if path.is_a?(String)
-
return if read_handle(path).nil?
-
write_handle(path[0..-2]).delete(path[-1])
-
end
-
-
3
def expand_path(*path)
-
1
%w[ama user state].push(*path)
-
end
-
-
3
def read_handle(*path)
-
expand_path(*path).reduce(node) do |cursor, segment|
-
cursor && cursor.key?(segment) ? cursor[segment] : nil
-
end
-
end
-
-
3
def write_handle(*path)
-
1
expand_path(*path).reduce(node.normal) do |cursor, segment|
-
3
cursor[segment] = {} unless cursor.key?(segment)
-
3
cursor[segment]
-
end
-
end
-
end
-
end
-
end
-
end
-
end