Class: RightScale::LoginPolicyFactory

Inherits:
Object
  • Object
show all
Defined in:
lib/right_infrastructure_agent/login_policy_factory.rb

Constant Summary collapse

INVALID_USERNAME_CHARS =

Regexp used to substitute sequences of non-username-friendly characters with a humble underscore. Technically we are more strict than the POSIX standard allows (a username could have any character in his name that is a valid filename character), but we want to have sane and legal usernames.

/[^a-z0-9_]+/
OLD_PUBLIC_KEY_BOUNDARY_AGE =

Number of seconds before a public key is considered old

24 * 60 * 60

Class Method Summary collapse

Class Method Details

.policy_for_account(account, terse = false) ⇒ Object

Create LoginPolicy for given account, including all users that are authorized to login, their public keys, etc. The policy is based on Permissions, UserCredentials and Settings of the account and is uniform across all instances of the account. Optionally omit public keys that have not been updated recently with the expectation that the receiver of this policy instead uses the fingerprint to determine whether it needs to request the update.

Parameters

account(Account)

Account that login policy should be built for

terse(Boolean)

Whether to omit a public key from the policy if it was created

more than OLD_PUBLIC_KEY_BOUNDARY_AGE seconds ago

Return

policy(RightScale::LoginPolicy) Policy specifying which users may login & some related metadata



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/right_infrastructure_agent/login_policy_factory.rb', line 36

def self.(, terse = false)
  # Find all users who are allowed and able to do managed login. Simultaneously build some indices
  # of timestamps authorizing perms, which will be used presently to create the policy object.

  # Build the policy and its list of users
  old_cred_boundary = Time.now - OLD_PUBLIC_KEY_BOUNDARY_AGE
  timestamps_max = Time.at(0)
  policy = LoginPolicy.new
  policy.exclusive  = .setting('managed_login_mandatory')

  # Do this as a closure around the timestamp and cred boundaries instead
  # of a method to avoid having to pass them back and forth constantly.
   = Proc.new do |u, is_superuser|
    cred_updated_at = Time.parse(u.cred_updated_at)
    timestamps_max = [timestamps_max, Time.parse(u.perm_updated_at), cred_updated_at].max

    # Currently there is only one public key per user but in the future there may be more
    # There should always be the exact number of fingerprints
    public_keys = [u.cred_public_value]
    processed_keys = []
    public_key_fingerprints = [u.cred_public_fingerprint]
    processed_fingerprints = []

    if terse && cred_updated_at < old_cred_boundary
      processed_keys = public_keys.map { |k| nil }
      processed_fingerprints = public_key_fingerprints
    else
      # Post-process the public keys to make sure they are well-formed AND contain a comment
      # at the end of the line. Technically the comment is optional, but a bug in RightImage
      # 5.1.1 - 5.6 makes the comment mandatory else the instance has heinous problems
      # when trying to update its managed login policy.
      public_keys.zip(public_key_fingerprints).each do |(k, f)|
        if (components = LoginPolicy.parse_public_key(k))
          components[3] ||= u.email
          processed_keys << components.compact.join(' ')
          processed_fingerprints << f
        else
          # In case this is some malformed or other weird key, omit its value
        end
      end
    end

    uuid        = u.id.to_s
    username    = u.email.split('@', 2).first.downcase.gsub(INVALID_USERNAME_CHARS,'_')
    common_name = u.email
    superuser   = is_superuser
    expires_at  = u.perm_deleted_at.nil? ? nil : Time.parse(u.perm_deleted_at)

    LoginUser.new(uuid, username, public_key = nil, common_name, superuser, expires_at, processed_keys,
                  profiled_data = nil, processed_fingerprints)
  end

  # Get list of all super users
  superusers = users_with_role(, 'server_superuser')

  # Build all users conditionally on inclusion of superusers list
  policy.users = users_with_role(, 'server_login').map do |u|
    .call(u, (superusers && superusers.include?(u)) || false )
  end

  policy.created_at = timestamps_max

  return policy
end

.policy_for_instance(instance, audit_id, terse = false) ⇒ Object

Create LoginPolicy for given instance, including all users that are authorized to login, their public keys, etc. Currently this just delegates to #policy_for_account since all instances of a given account have the same policy at a given time. This may change in the future, however.

Parameters

instance(Ec2Instance)

Instance that login policy should be built for

audit_id(Integer)

Audit identifier

terse(Boolean)

Whether to omit a public key from the policy if it was created

more than OLD_PUBLIC_KEY_BOUNDARY_AGE seconds ago

Return

policy(RightScale::LoginPolicy) Policy specifying which users may login & some related metadata



114
115
116
117
118
# File 'lib/right_infrastructure_agent/login_policy_factory.rb', line 114

def self.policy_for_instance(instance, audit_id, terse = false)
  policy = (instance., terse)
  policy.audit_id = audit_id
  return policy      
end

.users_with_role(account, role) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/right_infrastructure_agent/login_policy_factory.rb', line 120

def self.users_with_role(, role)
  User.all( :select     => "`users`.*, `user_credentials`.public_value as cred_public_value, " +
                           "`user_credentials`.updated_at as cred_updated_at, " +
                           "`user_credentials`.public_value_fingerprint as cred_public_fingerprint, " +
                           "`permissions`.updated_at as perm_updated_at, " +
                           "`permissions`.deleted_at as perm_deleted_at",
            :joins      => [:user_credential, :permissions],
            :conditions => ["permissions.account_id = ?  " +
                              "AND permissions.role_id = ?  " +
                              "AND (permissions.deleted_at IS NULL OR permissions.deleted_at > ?) " +
                              "AND user_credentials.public_value IS NOT NULL " +
                              "AND user_credentials.public_value <> ''",
                           .id, Role[role], Time.now.utc ])
end