Foreman and Organizations

This is awesome, Greg. Responses inline:

> 1. What are other objects that should be organizationally bound?
>
>
> - puppet classes
>
>
> - hosts
>
> I certainly think that hosts are bound to organizations, but I'm not so
sure about puppet classes. Puppet classes are often shared. Take a class
that installs NRPE to run remote Nagios checks; while the allows_hosts
(sorry about the specificity) might be different for each organization, the
package and service names are the same. Using smart variables or even just
parameters on a per-organization basis will allow the configuration to be
managed via a template while the class itself is unchanged across several
organizations. The example presented might very specific, but I think it's
a common desire to make classes generic enough to be shared. Therefore, we
should avoid keeping classes organization-specific.

How should user filters interact with organizational filtering?

This one is a bit tough. I think I would probably want to be able to search
through all users and then go to an organization and be able to find
members inside that org.

When a user tries to create Domain "ABC" in Organization "X" and Domain
> "ABC" already exists in Organization "Y", the user will receive an error
> saying that "ABC" already exists. However, the user cannot access Domain
> "ABC". How should the foreman deal with this situation?
>
> - indicate the that Domain already exists, but the user does not have
> access to the Domain in that organization?
>
>
> - alert administrators that a user tried to add Domain "ABC" to an
> different Organization, and that perhaps the admin needs to associate the
> existing "ABC" Domain with more orgs?
>
>
> - other ideas?
>
> I think the second one makes lot of sense. How about something along the
lines of "The domain 'ABC' is already registered. Please ask the
administrator to grant organization 'EFG' access to it."

Should there be a "default organization" setting a user can select that
> sets the default current organization for the user upon login?

Or we could present users with a list of the organizations they are a
member of and then they can just select which one they want to jump to?

Is there a better way to represent the config/settings.yaml changes?

I think it's fine for now. Curious about others' feedback, though.

How do organizations and user groups coexist, if at all?

User groups are a good way to manage and configure global permission levels
that are subsequently enforced within organizations. Say you have
permission to add new hosts. This should probably persist across
organizations, but there are certainly cases where a user might be only
able to edit hosts in one organization, but can only change domains in
another. Maybe the user groups are managed for each user on a
per-organization basis? If this is the way we decide to go then it probably
rename user groups to represent their per-org designation.

··· On Tue, Jul 24, 2012 at 4:41 PM, Greg Blomquist wrote:

Hi all,

I apologize for the long email.
tl;dr: a discussion on the “organizations” feature for foreman; how it’s
used, what’s changed in foreman, open questions.

I’ve been working on a feature for Foreman to add Organizations (aka,
Locations). The concept is to be able to group system objects together and
control access by users. You can see the code here:
https://github.com/theforeman/foreman/tree/orgs.

Background
*
*
With Organizations, an Admin can place system objects (Domains, Smart
Proxies, Compute Resources, etc…) into organizations and grant users
access to organizations. Users (non-admins) can only see system objects in
their organizations.

Setup
*
*
An installer can enable organizations for a foreman instance by setting
the “single_org” or “multi_org” foreman setting to true (in settings.yaml).
Foreman provides two ways to operate with organizations:

single_org: In single_org mode, an Admin can grant a user access to
several organizations, but a user must select a single organization from an
Organization Dropdown while working in foreman.
multi_org: In multi_org mode, an Admin grants a user access to several
organizations, and the user can see all system objects in all granted
organizations.

The single_org and multi_org modes do not control how many
organizations a user can belong to. Users can always belong to multiple
orgs regardless of this setting (see Current Organization(s) below).

This allows foreman to provide flexibility in managing organizations.

Administration

Administrators are not bound to organizations, so admins can see all
system objects regardless of organizational association. Users
(non-admins) are assigned to organizations. Users can only see the system
objects that are associated to their organizations.

Organizationally bound objects

  • Compute Resources
  • Domains
  • Environments
  • Hostgroups
  • Installation Media
  • Smart Proxies

Organizations and Users
*
*
When organizations are enabled, Users (specifically, non-admins) can
belong to multiple organizations. If organizations are enabled, then users
can only see system objects in their organizations. If a user is created
and not granted access to any organizations, the user will not see any
organizationally bound objects. If a system object is created and not
associated to any organizations, then no user will see the object (not even
those users who have no organization association).

Current Organization(s)

In single_org mode, if a user is associated with only one organization,
that organization becomes the user’s “current organization”. The current
organization provides foreman with the correct context for scope when
retrieving organizationally bound objects. If the user is associated with
multiple organizations, the user can select the current organization from
an organization drop down added to the topbar context. For the user in
multiple organizations, the default current organization is
User.current.organizations.first.

When creating system objects in single_org mode, the user’s current
organization is automatically associated with the system object being
created. For instance, when a user creates Domain “ABC” and has
Organization “X” currently selected. Domain “ABC” is automatically
associated with Organization “X”.

In multi_org mode, the current organizations is the list of
organizations the user is associated with. The user will be able to see
all the object associated to all of their organizations without having to
select a current organization.

When creating system objects in multi_org mode, the user can select to
add the system object to any of their organizations via an "Organizations"
tab on the edit view.

Implementation

There are four key points of implementation:

  • enabling organizations
  • tracking current organization(s)
  • changes to views
  • updating the default_scope for model associations

Enabling organizations
*
*
The config/settings.yaml file contains two new settings: single_org and
multi_org. These settings are mutually exclusive. They can both be
false, but they can’t both be true. To enable organizations, the
installer should set either single_org or multi_org to true.

Enabling organizations will enable the routes for organizations as well as
enable queries based on organization associations.

I haven’t tested enabling organizations, using organizations, then
disabling organizations.

Tracking current organization(s)

In single_org mode, the current organization is tracked similar to the
current user; in a thread local variable. When a user (non-admin) logs in,
the first organization (User.current.organizations.first) is selected as
the current organization. When the user changes the current organization
using the organization drop down, the thread local variable is updated.

In multi_org mode, the current organizations is always
User.current.organizations.

Changes to views
*
*
An organization tab has been added to the form for system objects that are
organizationally bound. Administrators use the multi-checkbox on the
organizations tab to associated system objects to organizations.

Updating default_scope for model associations

In rails 3.0, the default_scope unnamed scope does not allow lambdas or
blocks. The default_scope is evaluated immediately upon definition
instead of being evaluated at the time of SQL composition. (This has been
fixed in rails 3.1 and moving forward:
https://rails.lighthouseapp.com/projects/8994/tickets/1812-default). In
order to restrict queries for organizationally bound system objects, the
processing of default_scope needed to change to allow a lambda to grab
the currently selected organization(s) based on the current user. This
change is implemented as an initializer monkey patch of
ActiveRecord::Base.current_scoped_methods.

The side effect of this monkey patch is that it’s not possible to stack
default_scopes that use lambdas and non-lambdas. For default_scopes that
use lambdas, all the default scoping should be contained in the lambda.

With lambdas available for default_scope, the model object associations
are fairly straightforward. The class method Organization.apply_org_scope will
scope the caller on its relationship with organizations through the current
organization(s). Each model object for organizationally bound system
objects has a default_scope definition that calls
Organization.apply_org_scope in addition to any previous default scoping
that was applied before the organizations feature.

Open questions

  1. What are other objects that should be organizationally bound?
    • puppet classes
    • hosts
  2. How should user filters interact with organizational filtering?
  3. When a user tries to create Domain “ABC” in Organization “X” and
    Domain “ABC” already exists in Organization “Y”, the user will receive an
    error saying that “ABC” already exists. However, the user cannot access
    Domain “ABC”. How should the foreman deal with this situation?
    • indicate the that Domain already exists, but the user does not
      have access to the Domain in that organization?
    • alert administrators that a user tried to add Domain “ABC” to an
      different Organization, and that perhaps the admin needs to associate the
      existing “ABC” Domain with more orgs?
    • other ideas?
  4. Should there be a “default organization” setting a user can select
    that sets the default current organization for the user upon login?
  5. Is there a better way to represent the config/settings.yaml changes?
  6. How do organizations and user groups coexist, if at all?
  7. More questions?

Thanks!


Greg

> This is awesome, Greg. Responses inline:

Seconded!

>> What are other objects that should be organizationally bound?
>
> I certainly think that hosts are bound to organizations, but I'm not so sure
> about puppet classes. Puppet classes are often shared. Take a class that
> installs NRPE to run remote Nagios checks; while the allows_hosts (sorry
> about the specificity) might be different for each organization, the package
> and service names are the same. Using smart variables or even just
> parameters on a per-organization basis will allow the configuration to be
> managed via a template while the class itself is unchanged across several
> organizations. The example presented might very specific, but I think it's a
> common desire to make classes generic enough to be shared. Therefore, we
> should avoid keeping classes organization-specific.

This is all true, but I'd extend it to say that we might want to make
this an optional idea, or perhaps make classes belong to multiple
organisations. I'd want to share my apache class around, but not my
puppetmaster class.

> I think the second one makes lot of sense. How about something along the
> lines of "The domain 'ABC' is already registered. Please ask the
> administrator to grant organization 'EFG' access to it."

Agreed.

>> Should there be a "default organization" setting a user can select that
>> sets the default current organization for the user upon login?
>
> Or we could present users with a list of the organizations they are a member
> of and then they can just select which one they want to jump to?

That could get irritating if you spend 90% of your time in one
organisation. A default setting sounds like a good option.

>> Is there a better way to represent the config/settings.yaml changes?
>
> I think it's fine for now. Curious about others' feedback, though.

organisations: false|single|multi ? Shouldn't be too hard to parse
that… maybe make "true" default to one of them (probably single I
guess)

>> How do organizations and user groups coexist, if at all?
>
> User groups are a good way to manage and configure global permission levels
> that are subsequently enforced within organizations. Say you have permission
> to add new hosts. This should probably persist across organizations, but
> there are certainly cases where a user might be only able to edit hosts in
> one organization, but can only change domains in another. Maybe the user
> groups are managed for each user on a per-organization basis? If this is the
> way we decide to go then it probably rename user groups to represent their
> per-org designation.

I think there's definite overlap here with the requirements Ohad was
gathering for a re-vamp of the users/groups/roles codebase a few days
ago. We should probably bring those two together - it sounds like this
feature would solve some of my requirements I sent as feedback to
that.

Awesome work, Greg! Can't wait to have to play with it.

Cheers,
Greg

··· On 25 July 2012 03:03, Sam Kottler wrote: