Module: Xolo::Server::Helpers::Auth

Defined in:
lib/xolo/server/helpers/auth.rb

Constant Summary collapse

NO_AUTH_ROUTES =

these routes don’t need an auth’d session

[
  '/ping',
  '/auth/login',
  '/default_min_os'
].freeze
NO_AUTH_PREFIXES =

these route prefixes don’t need an auth’d session

[
  '/ping/'
].freeze
INTERNAL_ROUTES =

these routes are expected to be called by the xolo server itself and will have the internal_auth_token in the headers and will come from IPV4_LOOPBACK

We use routes like this for internal tasks that require a server-request context.

[
  '/maint/cleanup-internal'
].freeze
SERVER_ADMIN_ROUTES =

these routes must

[
  '/maint/threads',
  '/maint/state',
  '/maint/cleanup',
  '/maint/update-client-data',
  '/maint/rotate-logs',
  '/maint/set-log-level',
  '/maint/shutdown-server'
].freeze
PATCH_TITLE_UPDATED_WEBHOOK_ROUTE =

the route used by Jamf Webhooks to notify Xolo of Patch Title updates

'/subscribed-title-updates'
IPV4_LOOPBACK =

The loopback address for IPV4, aka ‘localhost’

'127.0.0.1'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(includer) ⇒ Object

when this module is included



68
69
70
# File 'lib/xolo/server/helpers/auth.rb', line 68

def self.included(includer)
  Xolo.verbose_include includer, self
end

.internal_auth_token_headerString

If a request comes in from one of our known IP addresses with a valid internal_auth_token in the headers, then the request is allowed.

This allows the xolo server to send requests to itself without needing to authenticate, as is needed for some kinds of maintenance tasks such as cleanup.

The token value is generated anew at startup and is a long random string, it is only available to the xolo server itself from its memory, and is never stored.

Returns:

  • (String)

    The internal_auth_token to be used in the Authorization header of requests



85
86
87
# File 'lib/xolo/server/helpers/auth.rb', line 85

def self.internal_auth_token_header
  @internal_auth_token_header ||= "Bearer #{SecureRandom.hex(64)}"
end

.jamf_webhook_auth_token_headerString

Returns The auth token to be used in the Authorization header of webhook event requests.

Returns:

  • (String)

    The auth token to be used in the Authorization header of webhook event requests



91
92
93
# File 'lib/xolo/server/helpers/auth.rb', line 91

def self.jamf_webhook_auth_token_header
  @jamf_webhook_auth_token_header ||= "Bearer #{Xolo::Server.config.subscription_webhook_token}"
end

Instance Method Details

#allowed_to_release_to_all?(admin_name) ⇒ Boolean

is the given username a member of the release_to_all_approval_group? If not, they are not allowed to set a title’s release_groups to ‘all’.

Parameters:

  • admin_name (String)

    The jamf acct name of the person

Returns:

  • (Boolean)

    Is the admin allowed to set release_groups to all?



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/xolo/server/helpers/auth.rb', line 204

def allowed_to_release_to_all?(admin_name)
  log_debug "Checking if '#{admin_name}' is allowed to release to all"

  groupname = Xolo::Server.config.release_to_all_jamf_group
  if groupname.pix_empty?
    log_debug 'No release_to_all_jamf_group defined, allowing all admins to release to all'
    return true
  end

  if user_in_jamf_acct_group?(groupname, admin_name)
    log_debug "'#{admin_name}' is allowed to release to all"
    true
  else
    log_debug "'#{admin_name}' is not allowed to release to all"
    false
  end
ensure
  jamf_cnx&.disconnect
end

#authenticated_via_jamf?(admin, pw) ⇒ Boolean

Try to authenticate the jamf user trying to log in to xolo. This must be a regular Jamf user acct, not an API client.

Parameters:

  • admin (String)

    The jamf acct name of the person seeking access

  • pw (String)

    The password for the jamf acct

Returns:

  • (Boolean)

    Did the password work for the user?



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/xolo/server/helpers/auth.rb', line 251

def authenticated_via_jamf?(admin, pw)
  log_debug "Checking Jamf authentication for admin '#{admin}'"
   = Jamf::Connection.new(
    host: Xolo::Server.config.jamf_hostname,
    port: Xolo::Server.config.jamf_port,
    verify_cert: Xolo::Server.config.jamf_verify_cert,
    open_timeout: Xolo::Server.config.jamf_open_timeout,
    timeout: Xolo::Server.config.jamf_timeout,
    user: admin,
    pw: pw
  )
  .disconnect
  true
rescue Jamf::AuthenticationError
  false
end

#internal_ip_ok?Boolean

Returns Is the request coming from one of our known IP addresses?.

Returns:

  • (Boolean)

    Is the request coming from one of our known IP addresses?



128
129
130
131
132
# File 'lib/xolo/server/helpers/auth.rb', line 128

def internal_ip_ok?
  # server_ip_addresses.include? request.ip
  # always require the request to come from the loopback address
  request.ip == Xolo::Server::Helpers::Auth::IPV4_LOOPBACK
end

#internal_token_ok?Boolean

Returns Is the internal_auth_token in the headers of the request?.

Returns:

  • (Boolean)

    Is the internal_auth_token in the headers of the request?



122
123
124
# File 'lib/xolo/server/helpers/auth.rb', line 122

def internal_token_ok?
  request.env['HTTP_AUTHORIZATION'] == Xolo::Server::Helpers::Auth.internal_auth_token_header
end

#jamf_webhook_auth_token_ok?Boolean

Returns Is the jamf webhook auth token in the headers of the request?.

Returns:

  • (Boolean)

    Is the jamf webhook auth token in the headers of the request?



142
143
144
# File 'lib/xolo/server/helpers/auth.rb', line 142

def jamf_webhook_auth_token_ok?
  request.env['HTTP_AUTHORIZATION'] == Xolo::Server::Helpers::Auth.jamf_webhook_auth_token_header
end

#member_of_admin_jamf_group?(admin_name) ⇒ Boolean

is the given username a member of the admin_jamf_group? or the server_admin_jamf_group? If not, they are not allowed to talk to the xolo server.

Parameters:

  • admin_name (String)

    The jamf acct name of the person seeking access

Returns:

  • (Boolean)

    Is the admin a member of the admin_jamf_group?



154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/xolo/server/helpers/auth.rb', line 154

def member_of_admin_jamf_group?(admin_name)
  log_info "Checking if '#{admin_name}' is allowed to access the Xolo server"

  groupname = Xolo::Server.config.admin_jamf_group
  return true if user_in_jamf_acct_group?(groupname, admin_name)

  # if they're not in the admin group, check the server_admin group
  return true if member_of_server_admin_jamf_group?(admin_name)

  log_info "'#{admin_name}' is not a member of the admin_jamf_group or the server_admin_jamf_group"
  false
end

#member_of_server_admin_jamf_group?(admin_name) ⇒ Boolean

is the given username a member of the server_admin_jamf_group? they must be in order to access the server admin routes

Parameters:

  • admin_name (String)

    The jamf acct name of the person seeking access

Returns:

  • (Boolean)

    Is the admin a member of the server_admin_jamf_group?



174
175
176
177
178
179
180
181
182
183
184
# File 'lib/xolo/server/helpers/auth.rb', line 174

def member_of_server_admin_jamf_group?(admin_name)
  return false unless Xolo::Server.config.server_admin_jamf_group

  log_info "Checking if '#{admin_name}' is allowed to access server admin routes"

  groupname = Xolo::Server.config.server_admin_jamf_group
  return true if user_in_jamf_acct_group?(groupname, admin_name)

  log_info "'#{admin_name}' is not a member of the server_admin_jamf_group '#{groupname}'"
  false
end

#server_ip_addressesArray<String>

Returns The IP addresses of this server.

Returns:

  • (Array<String>)

    The IP addresses of this server



136
137
138
# File 'lib/xolo/server/helpers/auth.rb', line 136

def server_ip_addresses
  Socket.ip_address_list.map(&:ip_address)
end

#user_in_jamf_acct_group?(groupname, username) ⇒ Boolean

check to see if a username is a member of a Jamf AccountGroup either from Jamf or from LDAP

Returns:

  • (Boolean)


226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/xolo/server/helpers/auth.rb', line 226

def user_in_jamf_acct_group?(groupname, username)
  log_debug "Checking if '#{username}' is a member of the Jamf AccountGroup '#{groupname}'"

  # This isn't well implemented in ruby-jss, so use c_get directly
  # TODO: use JP API when ruby-jss supports it
  jgroup = jamf_cnx.c_get("accounts/groupname/#{groupname}")[:group]

  if jgroup[:ldap_server]
    Jamf::LdapServer.check_membership jgroup[:ldap_server][:id], username, groupname, cnx: jamf_cnx
  else
    jgroup[:members].any? { |m| m[:name] == username }
  end
rescue Jamf::NoSuchItemError
  false
end

#valid_internal_auth_token?Boolean

Is the internal_auth_token in the headers of the request? and is the request coming from one of our known IP addresses?

Returns:

  • (Boolean)

    Is this a valid request from the xolo server itself?



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/xolo/server/helpers/auth.rb', line 104

def valid_internal_auth_token?
  log_info "Checking internal auth token from #{request.ip}"

  if !internal_ip_ok?
    warning = "Invalid IP address for internal request: #{request.ip}"
  elsif !internal_token_ok?
    warning = "Invalid internal auth token '#{request.env['HTTP_AUTHORIZATION']}' from #{request.ip}"
  else
    log_info "Internal request for #{request.path} is valid"
    return true
  end

  log_warn "WARNING: #{warning}"
  halt 403, { status: 403, error: 'You do not have access to this resource' }
end

#valid_server_admin?Boolean

is the session a member of the server_admin_jamf_group, and has a valid session?

Returns:

  • (Boolean)


191
192
193
194
195
# File 'lib/xolo/server/helpers/auth.rb', line 191

def valid_server_admin?
  return true if session[:authenticated] && member_of_server_admin_jamf_group?(session[:admin])

  halt 403, { status: 403, error: 'You do not have access to that resource.' }
end