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

183 views Asked by At

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?

1

There are 1 answers

0
Jörg W Mittag On BEST ANSWER

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:

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