lib/redfish_client/root.rb in redfish_client-0.1.0 vs lib/redfish_client/root.rb in redfish_client-0.2.0
- old
+ new
@@ -1,55 +1,95 @@
# frozen_string_literal: true
+require "base64"
require "redfish_client/resource"
module RedfishClient
# Root resource represents toplevel entry point into Redfish service data.
# Its main purpose is to provide authentication support for the API.
class Root < Resource
# AuthError is raised if the user session cannot be created.
class AuthError < StandardError; end
- # Token authentication header.
- AUTH_HEADER = "X-Auth-Token"
+ # Basic and token authentication headers.
+ BASIC_AUTH_HEADER = "Authorization"
+ TOKEN_AUTH_HEADER = "X-Auth-Token"
# Authenticate against the service.
#
# Calling this method will try to create new session on the service using
- # provided credentials. If the session creation fails, {AuthError} will be
- # raised.
+ # provided credentials. If the session creation fails, basic
+ # authentication will be attempted. If basic authentication fails,
+ # {AuthError} will be raised.
#
# @param username [String] username
# @param password [String] password
# @raise [AuthError] if user session could not be created
def login(username, password)
- r = self.Links.Sessions.post(
- payload: { "UserName" => username, "Password" => password }
- )
- raise AuthError unless r.status == 201
-
- logout
- rdata = r.data
- @connector.add_headers(AUTH_HEADER => rdata[:headers][AUTH_HEADER])
- @session = Resource.new(@connector, content: JSON.parse(rdata[:body]))
+ # Since session auth is more secure, we try it first and use basic auth
+ # only if session auth is not available.
+ if session_login_available?
+ session_login(username, password)
+ else
+ basic_login(username, password)
+ end
end
# Sign out of the service.
#
# If the session could not be deleted, {AuthError} will be raised.
def logout
- return unless @session
- r = @session.delete
- raise AuthError unless r.status == 204
- @session = nil
- @connector.remove_headers([AUTH_HEADER])
+ session_logout
+ basic_logout
end
# Find Redfish service object by OData ID field.
#
# @param oid [String] Odata id of the resource
# @return [Resource] new resource
def find(oid)
Resource.new(@connector, oid: oid)
+ end
+
+ private
+
+ def session_login_available?
+ !@content.dig("Links", "Sessions").nil?
+ end
+
+ def session_login(username, password)
+ r = self.Links.Sessions.post(
+ payload: { "UserName" => username, "Password" => password }
+ )
+ raise AuthError, "Invalid credentials" unless r.status == 201
+
+ session_logout
+
+ payload = r.data[:headers][TOKEN_AUTH_HEADER]
+ @connector.add_headers(TOKEN_AUTH_HEADER => payload)
+ @session = Resource.new(@connector, content: JSON.parse(r.data[:body]))
+ end
+
+ def session_logout
+ return unless @session
+ r = @session.delete
+ raise AuthError unless r.status == 204
+ @session = nil
+ @connector.remove_headers([TOKEN_AUTH_HEADER])
+ end
+
+ def auth_test_path
+ @content.values.map { |v| v["@odata.id"] }.compact.first
+ end
+
+ def basic_login(username, password)
+ payload = Base64.encode64("#{username}:#{password}").strip
+ @connector.add_headers(BASIC_AUTH_HEADER => "Basic #{payload}")
+ r = @connector.get(auth_test_path)
+ raise AuthError, "Invalid credentials" unless r.status == 200
+ end
+
+ def basic_logout
+ @connector.remove_headers([BASIC_AUTH_HEADER])
end
end
end