How to search roles in ldap recursively with Net::LDAP in Ruby - ruby

I'm creating a self service with the possibility to grant application roles (defined in a meta [ldap]) for a user. Our structure in the meta is not uniform. It looks like this:
o=meta
ou=Firm
ou=AppRoles
ou=GitLab
cn=Admin
cn=User
ou=SAP
ou=SAPCRT
cn=Admin
cn=User
ou=SAPLST
ou=NW
cn=Admin
cn=User
ou=ST
cn=Admin
cn=User
etc...
So you see, the cn (Approle) is not always on the same level.
This is the code I have so far. It finds 'ou's like GitLab Admin and GitLab User. But I need to receive a list with Gitlab Admin, Gitlab User, SAP/SAPCRT Admin, SAP/SAPCRT User, SAP/SAPLST/NW Admin, and so forth.
base = 'ou=AppRoles,ou=Firm,o=META'
filter = Net::LDAP::Filter.begins('ou', query)
How can I setup Net::LDAP to filter/search recursively?

Not sure if this will print all cn's under AppRole, but with the "puts" command you will see the output, could you show us the return of this block of code?
def get_ldap_users(ldap_password)
filter = Net::LDAP::Filter.eq("ou", "AppRoles")
treebase = "dc=yourdomainhere"
get_ldap(ldap_password).search(:base => treebase, :filter => filter) do |entry|
puts "CN: #{entry.cn}"
end
end

Related

Puppet provider prefetch

I am writing a provider to generate self signed certificate using the certdog krestfield API.
I have implemented the create, destroy, exists? method and I can properly manage my certificate by making different call to the API.
I implemented puppet resource using the self.prefetch and self.instances methods. I can retrieve the properties of my resources to be aware of their current state.
My resource contain two sensitive types 'username' and 'password' who are required to make the API calls. I can't store those values on the filesystem and I want the 'puppet resource' command to ignore those types.
Currently when I run 'puppet apply' for the manifest:
certdog_certificate { 'tstpuppet':
ensure => present,
server => 'apiserver',
username => 'apiserver_username',
password => 'apiserver_password',
}
It returns:
Notice: /Stage[main]/Main/Certdog_certificate[tstpuppet]/username: defined 'username' as 'apiserver_username'
Notice: /Stage[main]/Main/Certdog_certificate[tstpuppet]/password: defined 'password' as 'apiserver_password'
Is there a way to hide sensitive types for puppet resources ? How should I process ?
I had to properly define my resource attributes.
The configurable data not part of the persistant state should be parameters as describe in the puppet documentation.
The attributes username and password are now define with newparam instead of newproperty as below.
module Puppet
Type.newtype(:certdog_certificate) do
#doc = 'Manage certificate using certdog REST API'
ensurable do
desc 'Create or remove a certificate'
newvalue(:present) do
provider.create
end
newvalue(:absent) do
provider.destroy
end
defaultto :present
end
newparam(:cert_name, namevar: true) do
desc 'Name of the certificate request'
end
newparam(:username) do
desc 'Username for Certdog API server'
end
newparam(:password) do
desc 'Password for Certdog API server'
end
newproperty(:server) do
desc 'Certdog API server address'
end
end
#john-bollinger Thanks for your explanation, I was missing an important concept of the custom types.

Reading Keycloak OmniAuth::AuthHash elements with Ruby 2.7

From the Keycloak authentication token, I read the OmniAuth::AuthHash elements to extract the user's name, email and roles.
Reading the name and email are quite easy based on the token retrieved through auth = request.env["omniauth.auth"] statement.
Digging into the token's hierarchy provides requested information:
user.name = auth.info.name
user.uuid = auth.uid
user.provider = auth.provider
user.email = auth.info.email
I use the same method to search for user's roles list:
roles = auth.extra.raw_info.resource_access provides the following AuthHash:
#<OmniAuth::AuthHash
BFS.SIS=#<OmniAuth::AuthHash
roles=#<Hashie::Array
["dataproducer",
"fsodataconsumer",
"sisdatasteward"]
>
>
BFS.SIS.DAL=#<OmniAuth::AuthHash
roles=#<Hashie::Array
["kd_getLoadReports",
"kd_createTables",
"kd_readTables",
"kd_deleteTables"]
>
>
BFS.SIS.DPS.KEYSTORE=#<OmniAuth::AuthHash
roles=#<Hashie::Array
["keymanagement_key_read",
"keymanagement_keystore_read"]
>
>
BFS.SIS.SMS=#<OmniAuth::AuthHash
roles=#<Hashie::Array
["kdDatasetInformation_read",
"codeLists_update",
"definedVariables_set_status_validation_in_progress",
"hierarchicalCodeLists_update",
"hierarchicalCodeLists_create",
"kdDatasetInformation_delete",
"kdDatasetInformation_update",
"kdDataStructureDefinitions_create",
"kdDataStructureDefinitions_update",
"kdDataStructureDefinitions_delete",
"kdDataStructureDefinitions_read",
"kdDatasetInformation_create",
"definedVariables_set_status_open_from_rejected"]
>
>
BFS.SIS.UI=#<OmniAuth::AuthHash
roles=#<Hashie::Array
["bfs.sis.portal"]
>
>
>
There is the issue: as key names contain a '.', I cannot continue accessing subkeys with the syntax key.subkey to retrieve the array of roles for the BFS.SIS and BFS.SIS.SMS keys.
How could I extract the arrays from these keys?
Thanks for your help!
Well, I can't dig into the hash key.subkey syntax, but I can enumerate the subkeys. And then I can check if some match with needed entries, and extract child roles.
Here is the solution I implemented:
auth = request.env["omniauth.auth"]
roles = Array.new
activities = auth.extra.raw_info.allowlists.statisticalActivities
resources_accesses = auth.extra.raw_info.resource_access
resources_accesses.each do |access|
puts access # Provides the resources_access hash
puts access[0] # Provides the resources_access label
puts access[1] # Provides the resources_access roles array
# Check if label matches needed entries
if ["BFS.SIS.SMS", "BFS.SIS", "BFS.SIS.SCHEDULER"].include? access[0].to_s
access[1].roles.each do |role|
# Store each role in the roles array
roles << role
end
end
end
This list of roles for a user will help building the list of abilities for CanCanCan gem.

puppet functions exec windows command line

Currently i am trying to automate the start mode in windows server services. i tried to use puppetlabs registry but realized that the module didn't work as i expected.
Basically i have list of windows services that i need to update on each server but on some servers, the services might not exist, but puppetlabs registry will just create the new key if it's not exist which is not the expected behaviour. By right, it should work as mentioned below:
Check whether the service is in the servers or not
If it does, then update the start mode as mentioned inside the manifest/hiera
If not exist, just do nothing and skip to the next service immediately
Based from what i knew, it seems the only way to check whether the service key exist or not is by using custom function. So i already tried to write some custom function using win32/registry, but was unsuccessful by getting some error such as Win32API not supported. Another way i can think of is using the reg command line to check whether the key exist or not. Here is the puppet code functions:
module Puppet::Parser::Functions
newfunction(:check_winservice_exist, :type => :rvalue) do |args|
service_name = args[0]
unless args.length > 0 then
raise Puppet::ParseError, ("check_winservice_exist(): wrong number of arguments (#{args.length}; must be > 0)")
end
command = "reg query HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\#{service_name} /f DisplayName"
result = system command
return result
#if result == true
# return result
#else
# return result
#end
end
end
When i run the simplified ruby scripts in command line, it works and return the expected value. But when i used above scripts as puppet custom functions, it always return empty.
This is my first time to write a puppet custom functions so i am not sure what i did wrong here. Please advise whether there are another alternative that i can use to resolve the issue or maybe advise on what i did wrong on the functions script
I managed to resolve this issue by using custom facter as suggested by Matt. Just sharing the custom facter scripts that i used. It might not be perfect as i am still not really proficient in using ruby.
require 'win32/registry'
Facter.add(:winservices) do
confine :kernel => "windows"
setcode do
keyname= 'SYSTEM\CurrentControlSet\Services'
access = Win32::Registry::KEY_ALL_ACCESS
arr = []
winservices_list = []
Win32::Registry::HKEY_LOCAL_MACHINE.open(keyname, access) do |reg|
service_lists = (reg.each_key { |key, wtime| arr.push key })
arr.each do |service|
service_key = "SYSTEM\\CurrentControlSet\\Services\\#{service}"
begin
Win32::Registry::HKEY_LOCAL_MACHINE.open(service_key, access) do |reg|
value = reg['Start']
winservices_list.push service
end
rescue
end
end
winservices_list
end
end
end
And it simply works just by adding simply checking whether the service name is in the array or not
if $service_name in $facts['winservices'] {
service { "${service_name}":
provider => 'windows',
enable => $start_real,
}
}

SocketStream: Accessing #session outside of /server/app.coffee

I'm just getting started with SocketStream. (v0.1.0) I created the file /app/server/auth.coffee with an exports.actions.login function. I'd like to access #session.setUserId in this file, but I'm have a hard time figuring out where #session lives and how to access it outside of /app/server/app.coffee
Here is my auth.coffee with comments where I'd like to access the session.
users = [
username: 'craig'
password: 'craig',
username: 'joe'
password: 'joe',
]
authenticate = (credentials, cb) ->
user = _.detect users, (user) ->
user.username == credentials.username and user.password == credentials.password
authenticated = true if user?
callback cb, authenticated
exports.actions =
login: (credentials, cb) ->
authenticate credentials, (user) ->
# here is where i'd like to set the userId like so:
# #session.setUserId credentials.username
callback cb user
Interesting you bring a question about sessions up at the moment as I've been re-writing a lot of this code over the last few days as part of SocketStream 0.2.
The good news is the #session variable will be back in 0.2 as I have found an efficient way to pass the session data through to the back end without having to use the ugly #getSession callback.
To answer your question specifically, the #session variable is simply another property which is injected into the export.actions object before the request is processed. Hence you cannot have an action called 'session' (though the name of this 'magic variable' will be configurable in the next release of 0.2).
The exports.authenticate = true setting does not apply in your case.
I'm interested to know how/why you'd like to use the #session object outside of your /app/server code.
I will be committing all the latest session code to the 0.2 preview branch on github in a few days time.
Hope that helps,
Owen
You get the current session only within your server-side code (app/server) using the #getCurrentSession method.
Also you have to add:
exports.authenticate = true
to that file.

Ruby: Dynamically defining classes based on user input

I'm creating a library in Ruby that allows the user to access an external API. That API can be accessed via either a SOAP or a REST API. I would like to support both.
I've started by defining the necessary objects in different modules. For example:
soap_connecton = Library::Soap::Connection.new(username, password)
response = soap_connection.create Library::Soap::LibraryObject.new(type, data, etc)
puts response.class # Library::Soap::Response
rest_connecton = Library::Rest::Connection.new(username, password)
response = rest_connection.create Library::Rest::LibraryObject.new(type, data, etc)
puts response.class # Library::Rest::Response
What I would like to do is allow the user to specify that they only wish to use one of the APIs, perhaps something like this:
Library::Modes.set_mode(Library::Modes::Rest)
rest_connection = Library::Connection.new(username, password)
response = rest_connection.create Library::LibraryObject.new(type, data, etc)
puts response.class # Library::Response
However, I have not yet discovered a way to dynamically set, for example, Library::Connection based on the input to Library::Modes.set_mode. What would be the best way to implement this functionality?
Murphy's law prevails; find an answer right after posting the question to Stack Overflow.
This code seems to have worked for me:
module Library
class Modes
Rest = 1
Soap = 2
def self.set_mode(mode)
case mode
when Rest
Library.const_set "Connection", Class.new(Library::Rest::Connection)
Library.const_set "LibraryObject", Class.new(Library::Rest::LibraryObject)
when Soap
Library.const_set "Connection", Class.new(Library::Soap::Connection)
Library.const_set "LibraryObject", Class.new(Library::Soap::LibraryObject)
else
throw "#{mode.to_s} is not a valid Library::Mode"
end
end
end
end
A quick test:
Library::Modes.set_mode(Library::Modes::Rest)
puts Library::Connection.class == Library::Rest::Connection.class # true
c = Library::Connection.new(username, password)

Resources