Programmatically test whether an arbitrary user has access to an arbitrary file - ruby

I'd like to be able to write tests or specs that state that user soandso can read or edit file such_and_such. Although I would ultimately like this to be in RSpec, I'm assuming it will either all boil down to Bash commands (neatly wrapped in Ruby methods, of course), since it doesn't seem to be available in Ruby's File class, or anything else I've looked at. I've been hunting around for something in Bash to do this, and can't find anything (and nothing in the File class looks useful either). (Note: File.owned? does not do what I want, as it only tests if the process running the Ruby code is the owner, not if any arbitrary user has rights to edit or read, so it's significantly different on two counts.)
Is there any way to do this, built into Bash? (Or Ruby, or some Ruby gem, that I missed?) Or do I need to build a system myself, by getting owner and group info for the file, as well as read, write, and execute bits for each, and then looking up members of the group (in case so_and_so is not the owner), and seeing if the desired permissions are available to soandso either through ownership or group membership?
I'm currently only concerned about doing this on Unix-like systems, although something not dependent on a Unix shell that would also run on Windows would be a nice bonus.
Also, I'm interested in testing actual files, so something like FakeFS is not (as far as I can see) useful to me. I'm not trying to test how my Ruby code will interact with a file system, I'm trying to verify that all the necessary operations can be performed on actual files and directories. (Again,) I want to be able to specify that soandso can edit (or read) file such_and_such, NOT specify that file such_and_such is owned by soandso. The whole point of this is not specifying ownership (since that's an implementation detail and may need to change to accommodate other requirements, such as security) but only specify what my application and users actually need to be able to do to/in the file system.

If the user running your Ruby script has the necessary permissions, you could try running a test with the specific user with sudo.
sudo -u soandso test -r such_and_such
-r tests readability, -w writability. The return code is 0 if the test is passed.
By the way, I think that since the feature you are looking for could only be available for the super-user (since figuring out whether someone else has enough permissions would need you to have read permissions at least), it makes sense that it is not readily available in Ruby File class.

I haven't tried it, but I wonder if the FileTest methods would work as they say they operate on the "effective user id".
File.readable?(file_name) → true or false Link
Returns true if the named file is readable by the effective user id of this process.
File.writable?(file_name) → true or false Link
Returns true if the named file is writable by the effective user id of this process.
That in conjunction with seteuid might do the trick.
Process::Sys.seteuid(integer) → nil Link
Set the effective user ID of the calling process to integer. Not available on all platforms.
There is also setegid for group stuff...

Parse Permissions with File::Stat and Etc Modules
While this isn't a pre-packaged solution, you should be able to inspect a given file where you have sufficient access for stat in order to get mode, uid, and gid information. You can then parse /etc/passwd and /etc/group in order to find out whether a given user would have sufficient permissions.
Some building blocks include:
# Get the file's octal mode.
File.stat('/etc/passwd').mode.to_s(8)
#=> "100644"
# Get the group ID assigned to the file.
File.stat('/etc/passwd').gid
#=> 0
# Get the username associated with the given GID.
Etc.getpwuid(0).name
#=> "root"
You could also use the id utility to get information about a given user. For example:
%x{id root}
#=> "uid=0(root) gid=0(root) groups=0(root)\n"
but I think it would be easier to use the standard libraries rather than parse the output from id. YMMV.

Related

Ruby: how to access group info from /var/db/group.db

RHEL/CentOS 8.x, Ruby 2.5.5, irb 0.9.6
We have a specialized set up that keeps user and group information in /usr/local/etc/[user|group] and a custom Makefile to add that information to /var/db/[group|passwd].db accordingly.
I am trying to get the list of groups a user belongs to, in Ruby, after login.
I'm relatively new to the language, and have just read the documentation for the Etc module but that seems to exclusively work with the /etc/ filesystem. Not outrageous tbh.
Is there an easy way to access the Berkley DB info in /var/db or am I going to have to iterate through the /usr/local/etc/group file to get this information?
I suspect that the documentation of that module is heavily simplified, heavily outdated, or both. I am almost 100% sure that Etc will use the OS-provided standard library functions instead of going off and parsing system files by itself. (Why would the Ruby developers write parsers for the configuration files of every single OS they support, instead of just calling a POSIX function?)
You can confirm this suspicion using strace.
If you look at how the Ruby Etc module is structured, it maps 1:1 to the POSIX functions:
Etc::gegtrent: gets the next group entry.
Etc::endgtrent: stops iterating the groups.
Etc::settrent: resets the iteration.
Here are the POSIX functions for comparison:
endgrent, getgrent, setgrent – group database entry functions
In my case, I am testing this on macOS (which already has specialized user handling), but furthermore, I tested it on my company laptop, which is macOS joined to a Microsoft ActiveDirectory Domain. My user account and its group aren't even mentioned in either /etc/passwd or /etc/group, and yet, I can easily read them with Etc. I am very sure that the Ruby developers did not implement ActiveDirector just to accommodate my personal weird use case, so it must use system functions.
So, if you have e.g. a uid and want to know what groups it belongs to, you need to first get its name, and then search the groups. Unfortunately, it looks like the provided iterator wrappers do not support the common idiom that not supplying the block returns an Enumerator, so you have to create one yourself.
require 'etc'
uid = 0 # for example
username = Etc.getpwuid(uid).name
groups = Etc.enum_for(:group).select {|group| group.mem.include?(username) }
p groups

External Configuration for Standalone Ruby Script

I have a standalone ruby script that is intended to be run from a commandline.
Currently I have a section to define stuff like paths to files and such.
key_path = "C:\\OpenSSL-Win64\\keys\\"
consumer_file = "henrb028.consumer"
key_file = "henrb028.key"
I'd love to pull all of that out of the .rb file and into some external config file. Ideally it would be as lightweight as possible. For example, just copy-paste that into another ruby file and then cram it in at runtime.
I've tried both require and include and gotten varying errors. Without resorting to something like YAML, what's an easy and robust way to do this?
There are a few standard ways to realize what you describe.
Read from environmental variables
Each of those environmental stores a particular parameter. Then users can control, if they want to change some or all of the parameters.
Pros: Standard and handy way for both developers and users, providing the parameters are seldom changed. The default parameters can be easily shared in the same environment (like the same account or even platform-wide), as well as with other programs.
Cons: Somewhat obscured. For example, if a user's work with the tool is logged in a file, other people (or even the user her/himself later) cannot tell what the exact set of the parameters used was from the logfile. Or, if a user wants to change a parameter frequently, it is awkward.
An example:
key_file = ENV['KEY_FILE'] || "henrb028.key"
Read from a configuration file
Then either or both of each user and the system administrator can control and change it, when needed. How to determine the filename of the configuration file varies.
Pros: Suitable to store a large set of parameters.
Cons: Somewhat obscured. For example, if a user's work with the tool is logged in a file, other people (or even the user her/himself later) cannot tell what the exact set of the parameters used was. If a user wants to change a parameter frequently, it is very awkward.
A (crude!) example:
Suppose /etc/OUR_CONFIG.txt is as follows,
CONSUMER_FILE: henrb028.consumer
KEY_FILE: henrb028.key
Then, read them like,
opts = { 'KEY_FILE' => 'default.key' }
IO.readlines("/etc/OUR_CONFIG.txt").each do |ec|
a = ec.strip.split(/\s*:\s*/)
opts[a[0]] = a[1]
end
p opts
Specify with command-line options
If some (or all) options are not specified at the run time, it should fall back to the default value. OptionParser (as #tadaman suggested in his/her comment) is a powerful and versatile library to handle command-line options.
Pros: Standard and handy way for users, especially for a quick change at the run time. If you see the log file, you know what exactly the user did.
Cons: The default parameters cannot be shared with other tools on its own. To circumvent the problem, there are a few ways to make a fall-back routine:
Simply hard-code the default value in the main code.
Store the default value in an environmental variable.
Store the default value in a configuration file. The developer must decide how to determine the filename of the configuration file (hard-coding, environmental variable, etc).
A crude example (without using OptionParser):
opts = { 'key_file' => 'default.key' }
%w(consumer_file key_file).each do |ec|
cands = ARGV.grep(/^--#{Regexp.quote ec}=.+/)
opts[ec] = cands[0].split('=')[1] unless cands.empty?
end
p opts
I think these are the most typical ways, though there are other ways!

Validate input and prevent code injection

I have a general software security question.
I have an application. This application can receive a file as an input and write it to the disk.
This file is a text file and i want to make sure the user is not trying to write a script that will be saved on the disk.
FYI - operating system is Linux.
What i have so far is:
1) I can write it with limited permissions. (i do not think this is secure enough)
2) I want to verify this is not a script. << How can i do this?
Set a unprivileged user and write that file disabling execution and reading for other user i think it's the best option.
The second option is impracticable , but you can try to make things difficult to an attacker:
Remove some special delimiters, e.g. PHP's
Remove (if you can)some special characters used normally, e.g. {},<>, etc
Remove any non printablechar (for partially-avoiding any binaryfile)

What exactly is going on in Proc::Background?

I am trying to write a script that automates other perl scripts. Essentially, I have a few scripts that rollup data for me and need to be run weekly. I also have a couple that need to be run on the weekend to check things and email me if there is a problem. I have the email worked out and everything but the automation. Judging by an internet search, it seems as though using Proc::Background is the way to go. I tried writing a very basic script to test it and can't quite figure it out. I am pretty new to Perl and have never automated anything before (other than through windows task scheduler), so I really don't understand what the code is saying.
My code:
use Proc::Background;
$command = "C:/strawberry/runDir/SendMail.pl";
my $proc1 = Proc::Background -> new($command);
I receive an error that says no executable program located at C:... Can someone explain to me what exactly the code (Proc::Background) is doing? I will then at least have a better idea of how to accomplish my task and debug in the future. Thanks.
I did notice on Proc::Background's documentation the following:
The Win32::Process module is always used to spawn background processes
on the Win32 platform. This module always takes a single string
argument containing the executable's name and any option arguments.
In addition, it requires that the absolute path to the executable is
also passed to it. If only a single argument is passed to new, then
it is split on whitespace into an array and the first element of the
split array is used at the executable's name. If multiple arguments
are passed to new, then the first element is used as the executable's
name.
So, it looks like it requires an executable, which a Perl script would not be, but "perl.exe" would be.
I typically specify the "perl.exe" in my Windows tasks as well:
C:\dwimperl\perl\bin\perl.exe "C:\Dropbox\Programming\Perl\mccabe.pl"

Should ask for input or ask for modifying global variables in script file?

I have a small python script to make an installation. I need some information like username, password, path, classname, dbname, ip, port etc... First of all this is an admin task and end-users won't be dealing with this script. So for an administrator should I ask every needed information with console prompts or just mention them in readme file then he can go and change variable assignments in script file (which are on top with needed explanation).
Asking for every possible information is for me kinda chatty. And other approach might be a bit hidden... Yes, at this point I need ideas...
I think either through command line arguments, like
$ foo john password -p 1977 -h 255.255.255.0
Or, if there's simply too much information, filing it in the script is a much better idea, as you only have to do it once, and the script is less complex as a result (no stdin code).
Depending on how you distribute the script, you code have a config file with it, like '.foo_config', and get the config from there, that's much more explicit, but not as simple to distribute.

Resources