Skip to content

refactor: user extraction - #956

Open
Tinyblargon wants to merge 1 commit into
dev-sec:masterfrom
Tinyblargon:refactor-user-filter
Open

refactor: user extraction#956
Tinyblargon wants to merge 1 commit into
dev-sec:masterfrom
Tinyblargon:refactor-user-filter

Conversation

@Tinyblargon

@Tinyblargon Tinyblargon commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Refactored the user extraction logic to only use jinja filters instead of loops.
Previously the whole list of all users would be printed to the terminal 3 times.
Now we don't print any, removing clutter from the task log.
Not sure if the logging was intentional or an unexpected side-effect.

Tested on a default Debian 13 installation and both implementation returned the same results.
Did some benchmarking on a default Debian 13 and the jinja is ~15% faster than the loops.
On a default Debian 13 with 1000 normal users the jinja is ~470% faster than the loops.

don't log all users to the terminal 3 times

Signed-off-by: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com>
@Tinyblargon
Tinyblargon force-pushed the refactor-user-filter branch from 63739c2 to fa4ba5e Compare June 27, 2026 17:30

@schurzi schurzi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think i generally like this change. with some minor tweaks it should be equally readable and much nicer in implementation.

| dict2items
| rejectattr('key', 'in', os_always_ignore_users)
| rejectattr('key', 'in', os_ignore_users)
| json_query(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dislike a bit how we are abusing json_query here, do you think you can convert this to two calls for selectattr?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original data of the user is stored as a string array.
Inside a selectattr we can't cast to an int nor do greater/smaller then comparison, as it only works with strings (as far as I'm aware).
So I use the json_query only for the greater/smaller then comparison of the userid as we can do the | int cast in there.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is one more nuance to the usage of json_query the userid is stored at index 1 in the array.
With selectattr and rejectattr we can't index into an array.

All ways I've tries give an error:

  • selectattr('value.1', '>', 0)
  • selectattr('value[1]', '>', 0)
  • selectattr('value'[1], '>', 0)

My previous statement about greater/smaller then comparison is incorrect.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Tinyblargon, sorry for taking so long to respond. I really want to find a sensible way to get around using json_query. :)

I have given this some thought and our main problem here is that getent does not return the value for uid as int. Maybe we could solve that by creating a temporary list with the correct datatype:

- name: Extract accounts from local user database
  ansible.builtin.set_fact:
    system_users: >-
      {{ all_users
         | rejectattr('0', 'in', os_always_ignore_users)
         | rejectattr('0', 'in', os_ignore_users)
         | selectattr('1', 'gt', 0)
         | selectattr('1', 'le', os_auth_sys_uid_max | int)
         | map(attribute='0') | list }}
    regular_users: >-
      {{ all_users
         | rejectattr('0', 'in', os_always_ignore_users)
         | rejectattr('0', 'in', os_ignore_users)
         | selectattr('1', 'ge', os_auth_uid_min | int)
         | selectattr('1', 'le', os_auth_uid_max | int)
         | map(attribute='0') | list }}
    root_users: >-
      {{ all_users | selectattr('1', 'eq', 0) | map(attribute='0') | list }}
  vars:
    all_users: >-
      {{ ansible_facts.getent_passwd.keys() | zip(ansible_facts.getent_passwd.values() | map(attribute=1) | map('int')) | list }}

note: I did just some cursory tests on that and it seems to work

WDYT? Also @rndmh3ro you also have very nice oppinions about this kind of stuff. :D

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All (including the current) implementations are hard to read, so I'm impartial there. @schurzi, your solution looks the most readable, however it should get some comments what each of the matches do.

| dict2items
| rejectattr('key', 'in', os_always_ignore_users)
| rejectattr('key', 'in', os_ignore_users)
| json_query(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dislike a bit how we are abusing json_query here, do you think you can convert this to two calls for selectattr?

Comment thread .ansible/.lock

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also seems checked in by accident, can you remove it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants