sophos_central_api 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de42982e7073acef13887732efb277b3f24e31fcf34552f7857100edc20b99a2
4
- data.tar.gz: 40425c2083e372ea6434f86723209ddd1673a1055ca8fc26e9bac997fb961305
3
+ metadata.gz: d47cbadb0dff8019efbbac91b9690e5155653a24ae60eeb724fff45edaa90d87
4
+ data.tar.gz: a3a28af07f86c764b7fbfbcb03dcfaf47fce8ab23d30a8ddeea4d95407f8909f
5
5
  SHA512:
6
- metadata.gz: 4c3655439d962b32baef1e85a1ec31e4442c103472a82d0a2388d271fccde35c2fadb43088fd07db5ef3676d99331cea568f4019f61705b54a88577a7f97e073
7
- data.tar.gz: 47cc68a252b5ad954f6bb6b48aae862b27e48ab35e0bacdb8c1fb605dd4d322d2af1325ec5457bef5ad6c970d20ac5f01c23e30705bd41f50c58a0c0e55c208b
6
+ metadata.gz: 1ddc293f17bf92585c4cd21517ca405ab985a7d848d73195b9a4e59649be8950ff773faeba68cc21d895e4e3885600c9add869710cfcd6a68f65472152c34e09
7
+ data.tar.gz: 40c45c25bd5d6b4d8261b0fa39313f081e81ce350a2818732071a11b9f4347ceb7b3bb42adab53d39513dfca23563d55d01423d263f65cdfeab4b943c9fb6e20
data/CHANGELOG.md CHANGED
@@ -1,13 +1,26 @@
1
- ## [Unreleased]
1
+ # Changelog
2
2
 
3
3
  ## [0.1.0] - 2024-02-14
4
+
4
5
  - Initial release
5
6
 
6
7
  ## [0.1.1] - 2024-02-14
8
+
7
9
  - Convienience method added to get client for a teannt api host
8
10
 
9
11
  ## [0.1.2] - 2024-02-15
12
+
10
13
  - Fix initialization issue
11
14
 
12
15
  ## [0.2.0] - 2024-02-20
16
+
13
17
  - Configuration and authentication exceptions changed
18
+
19
+ ## [0.2.1] - 2024-07-17
20
+
21
+ - Fix issue with paging for endpoints
22
+
23
+ ## [0.2.2] - 2025-03-21
24
+
25
+ - Update docs, formatting
26
+
data/Gemfile CHANGED
@@ -8,4 +8,4 @@ gemspec
8
8
  gem 'rake', '~> 13.0'
9
9
  gem 'rubocop', '~> 1.7'
10
10
  gem 'simplecov', require: false, group: :test
11
- gem 'wrapi'
11
+ gem 'wrapi'
data/README.md CHANGED
@@ -1,11 +1,14 @@
1
1
  # Sophos Central Partner API
2
+
2
3
  [![Version](https://img.shields.io/gem/v/sophos_central_api.svg)](https://rubygems.org/gems/sophos_central_api)
3
4
  [![Maintainability](https://api.codeclimate.com/v1/badges/0e0c212559aad49a915c/maintainability)](https://codeclimate.com/github/jancotanis/sophos/maintainability)
4
5
  [![Test Coverage](https://api.codeclimate.com/v1/badges/0e0c212559aad49a915c/test_coverage)](https://codeclimate.com/github/jancotanis/sophos/test_coverage)
5
6
 
6
- This is a wrapper for the Sophos Central Partner API. You can see the API endpoints here https://developer.sophos.com/getting-started
7
+ This is a wrapper for the Sophos Central Partner API.
8
+ You can see the [API endpoints](https://developer.sophos.com/getting-started)
7
9
 
8
- Currently only the GET requests to customers, endpoints and alerts are implemented (readonly).
10
+ Currently only the GET requests to customers, endpoints and
11
+ alerts are implemented (readonly).
9
12
 
10
13
  ## Installation
11
14
 
@@ -17,17 +20,22 @@ gem 'sophos_central_api'
17
20
 
18
21
  And then execute:
19
22
 
20
- $ bundle install
23
+ ```console
24
+ > bundle install
25
+ ```
21
26
 
22
27
  Or install it yourself as:
23
28
 
24
- $ gem install sophos_central_api
29
+ ```console
30
+ > gem install sophos_central_api
31
+ ```
25
32
 
26
33
  ## Usage
27
34
 
28
- Before you start making the requests to API provide the client id and client secret and email/password using the configuration wrapping.
35
+ Before you start making the requests to API provide the client id and client secret
36
+ and email/password using the configuration wrapping.
29
37
 
30
- ```
38
+ ```ruby
31
39
  require 'sophos_central_api'
32
40
 
33
41
  Sophos.configure do |config|
@@ -45,20 +53,25 @@ end
45
53
  ```
46
54
 
47
55
  ## Resources
56
+
48
57
  ### Authentication
49
- ```
58
+
59
+ ```ruby
50
60
  # setup configuration
51
61
  #
52
62
  client.login
53
63
  ```
64
+
54
65
  |Resource|API endpoint|Description|
55
66
  |:--|:--|:--|
56
- |.login||
67
+ |.login|||
57
68
 
58
69
 
59
70
  ### Partner
60
- Endpoint for partner related requests
61
- ```
71
+
72
+ Endpoint for partner related requests
73
+
74
+ ```ruby
62
75
  roles = client.roles
63
76
  ```
64
77
 
@@ -69,14 +82,14 @@ roles = client.roles
69
82
  |.admins, .admin(id) |/partner/v1/admins/{id}|
70
83
  |.admin_role_assignments(admin_id)|/partner/v1/admins/{admin_id}/role-assignments|
71
84
  |.admin_role_assignment(admin_id,assignment_id)|/partner/v1/admins/{admin_id}/role-assignments/{assignment_id}|
72
- |.permission_sets |/partner/v1/roles/permission-sets|
73
- |.billing_usage(year, month) |/partner/v1/billing-usage/{year}/{month}|
74
-
85
+ |.permission_sets |/partner/v1/roles/permission-sets|
86
+ |.billing_usage(year, month) |/partner/v1/billing-usage/{year}/{month}|
75
87
 
76
88
  ### Common
89
+
77
90
  This is the OAS 3.0 specification for the Common API in Sophos Central.
78
91
 
79
- ```
92
+ ```ruby
80
93
  @client = Sophos.client()
81
94
  @client.login
82
95
  :
@@ -88,15 +101,17 @@ tenant = @client.tenant(id)
88
101
 
89
102
  |Resource|API endpoint|
90
103
  |:--|:--|
91
- |.alerts, .alert(id) |.../alerts/|
104
+ |.alerts, .alert(id) |.../alerts/|
92
105
  |.directory_user_groups, .directory_user_groups(id) |.../directory/user-groups|
93
- |.directory_user_group_users(id) |.../directory/user-groups/{id}/users|
94
- |.directory_users, .directory_user(id) |.../directory/users|
95
- |.directory_user_groups(id) |.../directory/users/{id}/groups|
106
+ |.directory_user_group_users(id) |.../directory/user-groups/{id}/users|
107
+ |.directory_users, .directory_user(id) |.../directory/users|
108
+ |.directory_user_groups(id) |.../directory/users/{id}/groups|
96
109
 
97
110
  ### Endpoints
111
+
98
112
  Returns endpoint for a provided tenant
99
- ```
113
+
114
+ ```ruby
100
115
  @client = Sophos.client()
101
116
  @client.login
102
117
  :
@@ -107,18 +122,18 @@ tenant = @client.tenant(id)
107
122
 
108
123
  |Resource|API endpoint|
109
124
  |:--|:--|
110
- |.downloads |.../downloads/|
111
- |.endpoint_groups, .endpoint_group(id) |.../endpoint-groupss/|
112
- |.migrations, .migration(id) |.../migrations|
113
- |.migration_endpoints(migration_id) |.../migrations/{migration_id}/endpoints|
114
- |.policies, .policy(id) |.../policies/{id}|
115
- |.endpoints |.../endpoints|
116
- |.endpoint_isolation(endpoint_id) |.../endpoints/{id}/isolation|
125
+ |.downloads |.../downloads/|
126
+ |.endpoint_groups, .endpoint_group(id) |.../endpoint-groupss/|
127
+ |.migrations, .migration(id) |.../migrations|
128
+ |.migration_endpoints(migration_id) |.../migrations/{migration_id}/endpoints|
129
+ |.policies, .policy(id) |.../policies/{id}|
130
+ |.endpoints |.../endpoints|
131
+ |.endpoint_isolation(endpoint_id) |.../endpoints/{id}/isolation|
117
132
  |.endpoint_tamper_protection(endpoint_id)|.../endpoints/{id}/tamper-protection|
118
133
 
119
134
  ## Contributing
120
135
 
121
- Bug reports and pull requests are welcome on GitHub at https://github.com/jancotanis/sophos.
136
+ Bug reports and pull requests are welcome on [GitHub](https://github.com/jancotanis/sophos).
122
137
 
123
138
  ## License
124
139
 
data/lib/sophos/api.rb CHANGED
@@ -1,30 +1,53 @@
1
- require "wrapi"
1
+ # frozen_string_literal: true
2
+
3
+ require 'wrapi'
2
4
  require File.expand_path('configuration', __dir__)
3
5
  require File.expand_path('authentication', __dir__)
4
6
 
5
7
  module Sophos
6
- # @private
7
-
8
+ # Handles API configuration, connections, and authentication.
9
+ # This class manages settings such as API keys, tokens, and endpoint configurations,
10
+ # and includes necessary modules for making authenticated API calls.
8
11
  class API
9
- # @private
10
- attr_accessor *Configuration::VALID_OPTIONS_KEYS
12
+ # Attribute accessors for configuration keys
13
+ # These keys include essential settings such as client ID, client secret, and API endpoint.
14
+ attr_accessor(*Configuration::VALID_OPTIONS_KEYS)
11
15
 
12
- # Creates a new API and copies settings from singleton
16
+ # Initializes a new API instance and applies configuration settings.
17
+ #
18
+ # @param options [Hash] A hash of options to configure the API.
19
+ # These options are merged with global configuration settings.
20
+ #
21
+ # @example Initialize a new API instance with custom options:
22
+ # api = Sophos::API.new(client_id: 'your_client_id', client_secret: 'your_client_secret')
23
+ #
13
24
  def initialize(options = {})
25
+ # Merge provided options with the global Sophos configuration
14
26
  options = Sophos.options.merge(options)
27
+
28
+ # Set instance variables for each valid configuration key
15
29
  Configuration::VALID_OPTIONS_KEYS.each do |key|
16
30
  send("#{key}=", options[key])
17
31
  end
18
32
  end
19
33
 
34
+ # Returns the current API configuration as a hash.
35
+ #
36
+ # @return [Hash] A hash of the current API configuration.
37
+ #
38
+ # @example Retrieve the current API configuration:
39
+ # api = Sophos::API.new
40
+ # api.config # => { client_id: 'abc', client_secret: 'xyz' }
41
+ #
20
42
  def config
21
43
  conf = {}
22
44
  Configuration::VALID_OPTIONS_KEYS.each do |key|
23
- conf[key] = send key
45
+ conf[key] = send(key)
24
46
  end
25
47
  conf
26
48
  end
27
49
 
50
+ # Including modules for API connection, request handling, and authentication
28
51
  include Configuration
29
52
  include WrAPI::Connection
30
53
  include WrAPI::Request
@@ -1,39 +1,53 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'faraday'
2
4
  require 'json'
3
5
  require 'uri'
4
6
  require File.expand_path('error', __dir__)
5
7
 
6
8
  module Sophos
7
- # Deals with authentication flow and stores it within global configuration
9
+ # Handles authentication flow for accessing Sophos APIs and stores tokens in global configuration.
10
+ # This module manages OAuth2-based token generation and sets up the API connection with authorization headers.
11
+ #
12
+ # @note Ensure that `client_id` and `client_secret` are configured before calling authentication methods.
13
+ # @see https://developer.sophos.com/getting-started
8
14
  module Authentication
9
15
 
10
- # Authorize to the Sophos portal and return access_token
11
- # @see https://developer.sophos.com/getting-started
12
- def auth_token(options = {})
16
+ # Authorizes with the Sophos portal and retrieves an access token.
17
+ # This method performs an OAuth2 client credentials flow, returning a valid token.
18
+ #
19
+ # @param _options [Hash] (optional) Any additional options for the request.
20
+ # @return [String] Access token for the Sophos API.
21
+ # @raise [ConfigurationError] If `client_id` or `client_secret` is not set.
22
+ # @raise [AuthenticationError] If authentication fails due to invalid credentials or other errors.
23
+ #
24
+ # @example Authenticate and retrieve a token:
25
+ # api.auth_token
26
+ #
27
+ def auth_token(_options = {})
13
28
  raise ConfigurationError.new 'Client id and/or secret not configured' unless client_id && client_secret
14
- # use id endpoint instead of global api
15
- response = connection.post(id_endpoint+'/api/v2/oauth2/token') do |request|
29
+
30
+ # POST request to obtain the OAuth2 token using client credentials
31
+ response = connection.post("#{id_endpoint}/api/v2/oauth2/token") do |request|
16
32
  request.headers['Content-Type'] = 'application/x-www-form-urlencoded'
17
- request.body = URI.encode_www_form( api_access_token_params )
33
+ request.body = URI.encode_www_form(api_access_token_params)
18
34
  end
35
+
19
36
  api_process_token(response.body)
37
+ setup_connection
20
38
 
21
- # use default endpoint and replace it with global endpoint
22
- partner = self.get( "/whoami/v1" )
23
- if 'partner'.eql?(partner.idType) && partner.id
24
- self.partner_id = partner.id
25
- self.endpoint = partner.apiHosts.global
26
- self.connection_options = { headers: { 'X-partner-id': self.partner_id } }
27
- else
28
- raise AuthenticationError.new 'Partner id not returned; response ' + response.to_s
29
- end
39
+ # Returns the generated access token
40
+ self.access_token
30
41
  rescue Faraday::UnauthorizedError => e
31
- raise AuthenticationError.new 'Unauthorized; response ' + e.to_s
42
+ raise AuthenticationError.new "Unauthorized; response #{e}"
32
43
  end
33
44
  alias login auth_token
34
45
 
35
- private
46
+ private
36
47
 
48
+ # Builds the request body parameters for the token request.
49
+ #
50
+ # @return [Hash] Parameters required for the client credentials grant type.
37
51
  def api_access_token_params
38
52
  {
39
53
  grant_type: 'client_credentials',
@@ -43,14 +57,36 @@ module Sophos
43
57
  }
44
58
  end
45
59
 
60
+ # Processes the token response and extracts authentication details.
61
+ #
62
+ # @param response [Hash] The HTTP response body containing the OAuth2 token data.
63
+ # @raise [AuthenticationError] If `access_token` is missing or invalid.
46
64
  def api_process_token(response)
47
- at = self.access_token = response['access_token']
48
- self.token_type = response['token_type']
49
- self.refresh_token = response['refresh_token']
50
- self.token_expires = response['expires_in']
51
- raise AuthenticationError.new 'Could not find valid access_token; response ' + response.to_s if at.nil? || at.empty?
65
+ self.access_token = response['access_token']
66
+ self.token_type = response['token_type']
67
+ self.refresh_token = response['refresh_token']
68
+ self.token_expires = response['expires_in']
52
69
 
53
- at
70
+ if self.access_token.nil? || self.access_token.empty?
71
+ raise AuthenticationError.new "Could not find valid access_token; response #{response}"
72
+ end
73
+ end
74
+
75
+ # Sets up the API connection using the authenticated token and partner-specific settings.
76
+ #
77
+ # This method retrieves the partner ID, sets the global endpoint, and configures headers.
78
+ #
79
+ # @raise [AuthenticationError] If the partner ID is not returned in the `whoami` response.
80
+ def setup_connection
81
+ partner = self.get("/whoami/v1")
82
+
83
+ if 'partner'.eql?(partner.idType) && partner.id
84
+ self.partner_id = partner.id
85
+ self.endpoint = partner.apiHosts.global
86
+ self.connection_options = { headers: { 'X-partner-id': self.partner_id } }
87
+ else
88
+ raise AuthenticationError.new "Partner id not returned; response #{partner}"
89
+ end
54
90
  end
55
91
  end
56
92
  end
@@ -1,25 +1,46 @@
1
- module Sophos
1
+ # frozen_string_literal: true
2
2
 
3
+ module Sophos
3
4
  class Client
4
5
  require File.expand_path('helper', __dir__)
5
6
  include Sophos::Client::Helper
6
7
 
7
- # This is the OAS 3.0 specification for the Common API in Sophos Central.
8
+ # Common API module for handling various endpoints in Sophos Central.
9
+ # This module provides access to alerts, directory users, and user groups.
8
10
  # @see https://developer.sophos.com/docs/common-v1/1/overview
9
11
  module Common
10
-
12
+ # Define API calls using helper methods for common endpoints.
13
+ # These methods automatically create GET calls to the specified URL.
14
+ #
11
15
  # @see https://developer.sophos.com/docs/common-v1/1/routes/alerts/get
12
16
  Helper::def_api_call :alerts, Helper::common_url(:alerts)
17
+
13
18
  # @see https://developer.sophos.com/docs/common-v1/1/routes/directory/user-groups/get
14
19
  Helper::def_api_call :directory_user_groups, Helper::common_url('directory/user-groups')
20
+
21
+ # Fetch paginated users in a specified user group.
22
+ # @param group_id [String] The ID of the user group.
23
+ # @param params [Hash] Additional query parameters (e.g., `pageSize`, `page`).
24
+ # @return [Hash] API response containing users in the specified group.
25
+ # @see https://developer.sophos.com/docs/common-v1/1/routes/directory/user-groups/get
15
26
  def directory_user_group_users(group_id, params = {})
16
27
  get_paged(Helper::common_url("directory/user-groups/#{group_id}/users"), params)
17
28
  end
29
+
30
+ # Define an API call to fetch all directory users.
31
+ # @see https://developer.sophos.com/docs/common-v1/1/routes/directory/users/get
18
32
  Helper::def_api_call :directory_users, Helper::common_url('directory/users')
33
+
34
+ # Fetch paginated user groups for a specified user.
35
+ # @param user_id [String] The ID of the user.
36
+ # @param params [Hash] Additional query parameters (e.g., `pageSize`, `page`).
37
+ # @return [Hash] API response containing groups the user belongs to.
38
+ # @see https://developer.sophos.com/docs/common-v1/1/routes/directory/users/get
19
39
  def directory_user_groups(user_id, params = {})
20
40
  get_paged(Helper::common_url("directory/users/#{user_id}/groups"), params)
21
41
  end
22
- # roles and admins can be retrieved using the partner api
42
+
43
+ # Additional methods for roles and admins can be retrieved using the partner API.
23
44
  end
24
45
  end
25
46
  end
@@ -1,40 +1,66 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sophos
2
4
  class Client
3
5
  require File.expand_path('helper', __dir__)
4
6
  include Sophos::Client::Helper
5
7
 
6
- # Sophos Endpoint api
8
+ # Module to interact with Sophos Endpoint API
7
9
  # @see https://developer.sophos.com/docs/endpoint-v1/1/overview
8
10
  module Endpoint
9
11
 
12
+ # Define API calls with dynamic helper methods for common endpoints.
13
+
14
+ # Fetch available software downloads.
10
15
  # @see https://developer.sophos.com/docs/endpoint-v1/1/routes/downloads/get
11
16
  Helper::def_api_call :downloads, Helper::endpoint_url(:downloads)
12
17
 
18
+ # Fetch all endpoint groups.
13
19
  # @see https://developer.sophos.com/docs/endpoint-v1/1/routes/endpoint-groups/get
14
20
  Helper::def_api_call :endpoint_groups, Helper::endpoint_url(:endpoint_groups), :endpoint_group
21
+
22
+ # Fetch endpoints belonging to a specific endpoint group.
23
+ # @param group_id [String] The ID of the endpoint group.
24
+ # @return [Array<Hash>] Paginated response with endpoint details.
15
25
  def endpoint_group_endpoints(group_id)
16
26
  get_paged Helper::endpoint_url("endpoint-groups/#{group_id}/endpoints")
17
27
  end
18
28
 
29
+ # Fetch details on endpoint migrations.
19
30
  # @see https://developer.sophos.com/docs/endpoint-v1/1/routes/migrations/get
20
31
  Helper::def_api_call :migrations, Helper::endpoint_url(:migrations), :migration
32
+
33
+ # Fetch endpoints involved in a specific migration.
34
+ # @param migration_id [String] The ID of the migration.
35
+ # @return [Array<Hash>] Paginated response with endpoints involved in the migration.
21
36
  def migration_endpoints(migration_id)
22
37
  get_paged Helper::endpoint_url("migrations/#{migration_id}/endpoints")
23
38
  end
24
39
 
40
+ # Fetch policy details for the tenant.
25
41
  # @see https://developer.sophos.com/docs/endpoint-v1/1/routes/policies/get
26
42
  Helper::def_api_call :policies, Helper::endpoint_url(:policies), :policy
27
43
 
28
- # Get all the endpoints for the specified tenant. No endpoint method defined as this clashes with api endpoint method
44
+ # Fetch all endpoints for the specified tenant.
45
+ # @note No specific `endpoint` method due to potential name conflicts with the base API method.
29
46
  # @see https://developer.sophos.com/docs/endpoint-v1/1/routes/endpoints/get
30
47
  Helper::def_api_call :endpoints, Helper::endpoint_url(:endpoints)
48
+
49
+ # Fetch the isolation status for a specific endpoint.
50
+ # @param endpoint_id [String] The ID of the endpoint.
51
+ # @return [Hash] Response with the isolation status of the endpoint.
52
+ # @see https://developer.sophos.com/docs/endpoint-v1/1/routes/endpoints/get
31
53
  def endpoint_isolation(endpoint_id)
32
54
  get Helper::endpoint_url("endpoints/#{endpoint_id}/isolation")
33
55
  end
56
+
57
+ # Fetch tamper protection settings for a specific endpoint.
58
+ # @param endpoint_id [String] The ID of the endpoint.
59
+ # @return [Hash] Response containing tamper protection details.
60
+ # @see https://developer.sophos.com/docs/endpoint-v1/1/routes/endpoints/get
34
61
  def endpoint_tamper_protection(endpoint_id)
35
62
  get Helper::endpoint_url("endpoints/#{endpoint_id}/tamper-protection")
36
63
  end
37
-
38
64
  end
39
65
  end
40
66
  end
@@ -1,49 +1,86 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sophos
2
-
3
- # Generate generic api methods
4
+ # Generate generic API methods dynamically for various Sophos APIs.
4
5
  class Client
5
6
  module Helper
6
7
 
7
- def self.sanitize method_name
8
- method_name.to_s.gsub('_', '-')
8
+ # Convert method names to URL-safe format (e.g., 'user_groups' => 'user-groups')
9
+ # @param method_name [Symbol, String] Method name to be sanitized
10
+ # @return [String] Sanitized method name for use in URL paths
11
+ def self.sanitize(method_name)
12
+ method_name.to_s.tr('_', '-')
13
+ end
14
+
15
+ # Generate a URL path for the common API
16
+ # @param method [Symbol, String] Method name for the API endpoint
17
+ # @return [String] Full path for the common API endpoint
18
+ def self.common_url(method)
19
+ url('common', method)
20
+ end
21
+
22
+ # Generate a URL path for the endpoint API
23
+ def self.endpoint_url(method)
24
+ url('endpoint', method)
9
25
  end
10
26
 
11
- def self.common_url(method) self.url('common', method); end
12
- def self.endpoint_url(method) self.url('endpoint', method); end
13
- def self.partner_url(method) self.url('partner', method); end
14
-
15
- def self.url(api,method)
27
+ # Generate a URL path for the partner API
28
+ def self.partner_url(method)
29
+ url('partner', method)
30
+ end
31
+
32
+ # Generic method to generate API URLs
33
+ # @param api [String] API type (e.g., 'common', 'endpoint', 'partner')
34
+ # @param method [Symbol, String] Endpoint method name
35
+ # @return [String] Full API URL path
36
+ def self.url(api, method)
16
37
  "/#{api}/v1/#{sanitize(method)}"
17
38
  end
18
39
 
19
- # generate end point for 'endpoint' and 'endpoints'
40
+ # Dynamically define API methods for both singular and plural endpoints.
41
+ # It supports paginated or non-paginated responses.
42
+ #
43
+ # @param method [Symbol] Plural method name (e.g., `:alerts`)
44
+ # @param url [String] Endpoint URL path
45
+ # @param singular_method [Symbol, nil] Singular method name (e.g., `:alert`), optional
46
+ # @param paged [Boolean] If true, generate paginated API calls (default: true)
20
47
  def self.def_api_call(method, url, singular_method = nil, paged = true)
21
48
  if singular_method
22
- self.send(:define_method, method) do |id = nil, params = {}|
23
- if id
24
- get("#{url}/#{id}", params)
25
- else
26
- get_paged(url, params)
27
- end
28
- end
29
- # strip trailing 's'
30
- self.send(:define_method, singular_method) do |id, params = {}|
31
- get("#{url}/#{id}", params)
32
- end
49
+ define_singular_and_plural_methods(method, url, singular_method)
33
50
  else
34
- if paged
35
- self.send(:define_method, method) do |params = {}|
36
- get_paged(url, params)
37
- end
38
- else
39
- self.send(:define_method, method) do |params = {}|
40
- get(url, params)
41
- end
42
- end
51
+ paged ? define_paged_method(method, url) : define_plain_method(method, url)
43
52
  end
44
53
  end
45
54
 
55
+ private
56
+
57
+ # Define both singular and plural methods.
58
+ # Example: `alerts` (for paginated results) and `alert(id)` (for single alert).
59
+ def self.define_singular_and_plural_methods(method, url, singular_method)
60
+ # Define plural method: paginated call if no ID, otherwise fetch singular resource.
61
+ define_method(method) do |id = nil, params = {}|
62
+ id ? get("#{url}/#{id}", params) : get_paged(url, params)
63
+ end
64
+
65
+ # Define singular method explicitly for single resource retrieval.
66
+ define_method(singular_method) do |id, params = {}|
67
+ get("#{url}/#{id}", params)
68
+ end
69
+ end
70
+
71
+ # Define a paginated method for list-based API endpoints.
72
+ def self.define_paged_method(method, url)
73
+ define_method(method) do |params = {}|
74
+ get_paged(url, params)
75
+ end
76
+ end
77
+
78
+ # Define a simple, non-paginated API method.
79
+ def self.define_plain_method(method, url)
80
+ define_method(method) do |params = {}|
81
+ get(url, params)
82
+ end
83
+ end
46
84
  end
47
85
  end
48
-
49
86
  end
@@ -1,44 +1,61 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sophos
2
4
  class Client
3
5
  require File.expand_path('helper', __dir__)
4
6
  include Sophos::Client::Helper
5
7
 
6
- # Sophos partner api
8
+ # Sophos Partner API module, providing access to tenants, roles, admins, and billing usage data.
7
9
  # @see https://developer.sophos.com/docs/partner-v1/1/overview
8
10
  module Partner
9
-
11
+ # Retrieve a list of tenants or a single tenant by ID.
10
12
  # @see https://developer.sophos.com/docs/partner-v1/1/routes/tenants/get
11
13
  Helper::def_api_call :tenants, Helper::partner_url(:tenants), :tenant
12
14
 
15
+ # Retrieve a list of roles or a single role by ID.
13
16
  # @see https://developer.sophos.com/docs/partner-v1/1/routes/roles/get
14
17
  Helper::def_api_call :roles, Helper::partner_url(:roles), :role
15
18
 
19
+ # Retrieve a list of admins or a single admin by ID.
16
20
  # @see https://developer.sophos.com/docs/partner-v1/1/routes/admins/get
17
21
  Helper::def_api_call :admins, Helper::partner_url(:admins), :admin
18
22
 
19
- # Get the list of role assignments for given admin.
23
+ # Retrieve the role assignments for a given admin.
24
+ # @param admin_id [String] Admin ID for whom role assignments are requested
25
+ # @param params [Hash] Optional query parameters
26
+ # @return [Hash] List of role assignments
20
27
  # @see https://developer.sophos.com/docs/partner-v1/1/routes/admins/%7BadminId%7D/role-assignments/get
21
28
  def admin_role_assignments(admin_id, params = {})
22
29
  get(Helper::partner_url("admins/#{admin_id}/role-assignments"), params)
23
30
  end
24
- # Get the list of role assignments for given admin.
31
+
32
+ # Retrieve a specific role assignment for an admin by assignment ID.
33
+ # @param admin_id [String] Admin ID
34
+ # @param assignment_id [String] Role assignment ID
35
+ # @param params [Hash] Optional query parameters
36
+ # @return [Hash] Details of the specific role assignment
25
37
  # @see https://developer.sophos.com/docs/partner-v1/1/routes/admins/%7BadminId%7D/role-assignments/%7BassignmentId%7D/get
26
38
  def admin_role_assignment(admin_id, assignment_id, params = {})
27
39
  get(Helper::partner_url("admins/#{admin_id}/role-assignments/#{assignment_id}"), params)
28
40
  end
29
41
 
30
-
31
- # List all the tenants for a partner
32
- def permission_sets( params = {} )
42
+ # Retrieve the list of permission sets.
43
+ # @param params [Hash] Optional query parameters
44
+ # @return [Array<Hash>] List of permission sets
45
+ # @see https://developer.sophos.com/docs/partner-v1/1/routes/roles/permission-sets/get
46
+ def permission_sets(params = {})
33
47
  get(Helper::partner_url('roles/permission-sets'), params)
34
48
  end
35
49
 
36
- # Usage report.
50
+ # Retrieve the billing usage report for a specific year and month.
51
+ # @param year [Integer] Year of the billing report (e.g., 2025)
52
+ # @param month [Integer] Month of the billing report (e.g., 1 for January)
53
+ # @param params [Hash] Optional query parameters
54
+ # @return [Hash] Billing usage data for the specified month
37
55
  # @see https://developer.sophos.com/docs/partner-v1/1/routes/billing/usage/%7Byear%7D/%7Bmonth%7D/get
38
56
  def billing_usage(year, month, params = {})
39
57
  get_paged(Helper::partner_url("billing/usage/#{year}/#{month}"), params)
40
58
  end
41
-
42
59
  end
43
60
  end
44
61
  end
data/lib/sophos/client.rb CHANGED
@@ -1,42 +1,67 @@
1
-
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Sophos
4
-
5
- # Wrapper for the Sophos Central REST API
4
+ # Wrapper for the Sophos Central REST API.
5
+ #
6
+ # This class acts as the main client for interacting with Sophos APIs and provides
7
+ # methods to create specific clients for tenants.
6
8
  #
7
- # @note All methods have been separated into modules and follow the same grouping used in api docs
9
+ # @note All methods are separated into modules based on the Sophos API documentation structure.
8
10
  # @see https://developer.sophos.com/getting-started
9
11
  class Client < API
10
12
  require File.expand_path('client/partner', __dir__)
11
13
 
12
- # get client to access api host for given tenant
13
- # @return [TenantClient]
14
+ # Returns a tenant-specific client to access the API host for the given tenant.
15
+ #
16
+ # @param tenant [Hash] The tenant object containing API host and tenant ID.
17
+ # @return [TenantClient, nil] A configured tenant client, or nil if the tenant is not provided.
18
+ #
19
+ # @example Access tenant-specific APIs:
20
+ # tenant_client = client(tenant)
21
+ # tenant_client.some_tenant_specific_api_call
14
22
  def client(tenant)
15
23
  tenant_client(tenant.apiHost, tenant.id) if tenant
16
24
  end
17
25
 
18
- # get teannt client to access api host for given tenant id
19
- # @return [TenantClient]
20
- def tenant_client( api_host, tenant_id )
21
- # create client and copy access_token and set default headers
22
- TenantClient.new(self.config.merge({
23
- access_token: access_token,
24
- endpoint: api_host,
25
- tenant_id: tenant_id,
26
- connection_options: { headers: { 'X-Tenant-ID': tenant_id } }
27
- }))
26
+ # Returns a configured tenant client to access APIs for a specific tenant ID and API host.
27
+ #
28
+ # @param api_host [String] The API host for the tenant.
29
+ # @param tenant_id [String] The tenant's unique ID.
30
+ # @return [TenantClient] A client configured with the tenant's API endpoint and headers.
31
+ #
32
+ # @example Create a tenant client and access tenant data:
33
+ # api_host = tenant.apiHost
34
+ # tenant_id = tenant.id
35
+ # tenant_client = tenant_client(api_host, tenant_id)
36
+ def tenant_client(api_host, tenant_id)
37
+ TenantClient.new(
38
+ self.config.merge(
39
+ {
40
+ access_token: access_token,
41
+ endpoint: api_host,
42
+ tenant_id: tenant_id,
43
+ connection_options: { headers: { 'X-Tenant-ID': tenant_id } }
44
+ }
45
+ )
46
+ )
28
47
  end
29
48
 
49
+ # Includes partner-related API methods.
30
50
  include Sophos::Client::Partner
31
51
  end
32
52
 
33
- # Wrapper for the Sophos Central REST API for tenant related calls
53
+ # Wrapper for the Sophos Central REST API for tenant-related calls.
34
54
  #
35
- # @note All methods have been separated into modules and follow the same grouping used in api docs
55
+ # This class inherits from `Sophos::Client` and includes additional modules
56
+ # for tenant-specific endpoints and common API methods.
57
+ #
58
+ # @note All tenant methods are separated into modules based on the Sophos API documentation.
36
59
  # @see https://developer.sophos.com/getting-started
37
60
  class TenantClient < Client
38
61
  require File.expand_path('client/common', __dir__)
39
62
  require File.expand_path('client/endpoint', __dir__)
63
+
64
+ # Includes modules for tenant-related and common API endpoints.
40
65
  include Sophos::Client::Common
41
66
  include Sophos::Client::Endpoint
42
67
  end
@@ -1,37 +1,46 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'wrapi'
2
4
  require File.expand_path('version', __dir__)
3
5
  require File.expand_path('pagination', __dir__)
4
6
 
5
7
  module Sophos
6
- # Defines constants and methods related to configuration
8
+ # Configuration module for setting API-related options and defaults.
9
+ #
10
+ # This module extends `WrAPI::Configuration` and defines additional constants,
11
+ # default values, and methods to handle the configuration of the Sophos API wrapper.
7
12
  module Configuration
8
13
  include WrAPI::Configuration
9
14
 
10
- # An array of additional valid keys in the options hash when configuring a [Sophos::API]
11
- VALID_OPTIONS_KEYS = (WrAPI::Configuration::VALID_OPTIONS_KEYS + [:partner_id, :tenant_id, :id_endpoint]).freeze
15
+ # Additional valid configuration keys for [Sophos::API].
16
+ VALID_OPTIONS_KEYS = (WrAPI::Configuration::VALID_OPTIONS_KEYS + %i[
17
+ partner_id tenant_id id_endpoint
18
+ ])
12
19
 
13
- # @private
14
- attr_accessor *VALID_OPTIONS_KEYS
20
+ attr_accessor(*VALID_OPTIONS_KEYS)
15
21
 
16
- DEFAULT_ENDPOINT = 'https://api.central.sophos.com'.freeze
17
- DEFAULT_ID_ENDPOINT = 'https://id.sophos.com'.freeze
18
- DEFAULT_UA = "Sophos Ruby API wrapper #{Sophos::VERSION}".freeze
22
+ # Default settings
23
+ DEFAULT_ENDPOINT = 'https://api.central.sophos.com'
24
+ DEFAULT_ID_ENDPOINT = 'https://id.sophos.com'
25
+ DEFAULT_USER_AGENT = "Sophos Ruby API wrapper #{Sophos::VERSION}"
19
26
  DEFAULT_PAGINATION = Sophos::RequestPagination::PagesPagination
20
27
  DEFAULT_PAGE_SIZE = 100
21
28
 
22
- # When this module is extended, set all configuration options to their default values
29
+ # Initialize configuration defaults when this module is extended.
23
30
  def self.extended(base)
24
31
  base.reset
25
32
  end
26
33
 
27
- # Create a hash of options and their values
34
+ # Returns the current configuration as a hash of key-value pairs.
35
+ #
36
+ # @return [Hash] Current configuration options.
28
37
  def options
29
- VALID_OPTIONS_KEYS.inject({}) do |option, key|
30
- option.merge!(key => send(key))
38
+ VALID_OPTIONS_KEYS.each_with_object({}) do |key, option|
39
+ option[key] = send(key)
31
40
  end
32
41
  end
33
42
 
34
- # Reset all configuration options to defaults
43
+ # Resets all configuration options to their default values.
35
44
  def reset
36
45
  super
37
46
  self.partner_id = nil
@@ -39,10 +48,9 @@ module Sophos
39
48
 
40
49
  self.endpoint = DEFAULT_ENDPOINT
41
50
  self.id_endpoint = DEFAULT_ID_ENDPOINT
42
- self.user_agent = DEFAULT_UA
51
+ self.user_agent = DEFAULT_USER_AGENT
43
52
  self.page_size = DEFAULT_PAGE_SIZE
44
53
  self.pagination_class = DEFAULT_PAGINATION
45
54
  end
46
55
  end
47
56
  end
48
-
data/lib/sophos/error.rb CHANGED
@@ -1,11 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sophos
2
-
3
- # Generic error to be able to rescue all Zabbix errors
4
+ # Base class for all custom errors in the Sophos module.
5
+ # Allows rescuing all Sophos-related exceptions with a single error class.
4
6
  class SophosError < StandardError; end
5
7
 
6
- # Raised when Zabbix not configured correctly
8
+ # Raised when configuration is incomplete or invalid.
7
9
  class ConfigurationError < SophosError; end
8
10
 
9
- # Error when authentication fails
11
+ # Raised when authentication to the Sophos API fails.
10
12
  class AuthenticationError < SophosError; end
11
- end
13
+
14
+ # Raised when API requests encounter general HTTP-related errors (optional future usage).
15
+ # Uncomment or extend if needed.
16
+ # class ApiError < SophosError; end
17
+ end
@@ -1,63 +1,77 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
 
3
5
  module Sophos
4
6
  # Defines HTTP request methods
5
7
  # required attributes format
6
8
  module RequestPagination
7
-
8
- # Sophos uses different pagination for global api and tenant api
9
+ # Defines HTTP request pagination logic for Sophos APIs.
10
+ # Sophos supports different pagination approaches for the global API and tenant APIs.
9
11
  #
10
- # response structure:
11
- # { "pages": {
12
- # "current": 1, # page no - not laways present
13
- # "size": 50,
14
- # "total": 3, # not always present - pages
15
- # "items": 123, # not always present - records
16
- # "maxSize": 100
12
+ # Expected response structure:
13
+ # {
14
+ # "pages": {
15
+ # "current": 1, # Current page number (optional)
16
+ # "size": 50, # Items per page
17
+ # "total": 3, # Total pages (optional)
18
+ # "items": 123, # Total items (optional)
19
+ # "maxSize": 100, # Maximum items per page allowed (optional)
20
+ # "nextKey": "abc123" # Key for fetching the next page, used when available
17
21
  # },
18
- # "items": []
19
- #
22
+ # "items": [] # Array of data items for the current page
23
+ # }
20
24
  class PagesPagination
21
25
  attr_reader :current, :total, :page_size
22
26
 
23
27
  def initialize(page_size)
24
- # ignore page size
25
28
  @page_size = page_size
26
- @total = @current = 1
29
+ @total = 1 # Default total pages
30
+ @current = 1 # Start at the first page
31
+ @next_key = nil # Key for fetching the next page if provided by the API
27
32
  end
28
33
 
34
+ # Returns options to be passed as query parameters for the next API request.
29
35
  def page_options
30
- { 'page': @current, 'pageSize': @page_size, 'pageTotal': true }
36
+ options = { page: @current, pageSize: @page_size, pageTotal: true }
37
+ options[:pageFromKey] = @next_key if @current > 1 && @next_key # wellicht nil zetten zonder && @next_key?
38
+ options
31
39
  end
32
40
 
41
+ # Updates the pagination state based on the API response data.
33
42
  def next_page!(data)
34
-
35
43
  pages = page_info(data)
36
44
  if pages
37
- @total = pages['total'].to_i
38
- if pages['current']
39
- @current = pages['current'].to_i + 1
40
- else
41
- @current += 1
42
- end
45
+ set_page_state(pages)
43
46
  else
44
- # no page info so assume single page request
47
+ # Assume a single-page request if no pagination info is available.
45
48
  @total = 0
46
49
  end
47
50
  end
48
51
 
49
- def page_info(body)
52
+ # Extracts the 'pages' metadata from the response body.
53
+ def page_info(body)
50
54
  body['pages']
51
55
  end
52
56
 
53
- def self.data(body)
54
- body['items'] ? body['items'] : body
57
+ # Extracts the relevant data items from the API response.
58
+ def self.data(body)
59
+ body['items'] || body
55
60
  end
56
61
 
57
- # only single page available
62
+ # Returns true if more pages are available.
58
63
  def more_pages?
59
64
  @current <= @total
60
65
  end
66
+
67
+ private
68
+
69
+ # Updates the current page, total pages, and next key (if present) from the pages metadata.
70
+ def set_page_state(pages)
71
+ @total = pages['total'].to_i if pages['total']
72
+ @current = pages['current'] ? pages['current'].to_i + 1 : @current + 1
73
+ @next_key = pages['nextKey'] if pages['nextKey']
74
+ end
61
75
  end
62
76
  end
63
77
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sophos
4
- VERSION = '0.2.0'
4
+ VERSION = '0.2.2'
5
5
  end
@@ -1,13 +1,38 @@
1
- require "wrapi"
1
+ # frozen_string_literal: true
2
+
3
+ require 'wrapi'
2
4
  require File.expand_path('sophos/api', __dir__)
3
5
  require File.expand_path('sophos/client', __dir__)
4
6
 
7
+ # Main module to interact with the Sophos API.
8
+ # This module provides access to the Sophos client, which handles API calls and manages configurations.
9
+ #
10
+ # @example Initialize a Sophos client:
11
+ # client = Sophos.client(
12
+ # client_id: 'your_client_id',
13
+ # client_secret: 'your_client_secret'
14
+ # )
15
+ #
5
16
  module Sophos
6
17
  extend Configuration
7
18
  extend WrAPI::RespondTo
8
19
 
20
+ # Initializes and returns a new instance of the Sophos client.
21
+ #
22
+ # @param options [Hash] A hash of options to configure the client.
23
+ # Supported options include:
24
+ # - `:client_id` [String] - Your Sophos API client ID.
25
+ # - `:client_secret` [String] - Your Sophos API client secret.
26
+ # - `:endpoint` [String] - The base URL for the API (default: Sophos Central endpoint).
27
+ #
28
+ # @return [Sophos::Client] An instance of the Sophos client with the provided configuration.
29
+ #
30
+ # @example Create a client with default settings:
31
+ # Sophos.client
32
+ #
33
+ # @example Create a client with custom options:
34
+ # Sophos.client(client_id: 'abc123', client_secret: 'xyz789')
9
35
  #
10
- # @return [Sophos::Client]
11
36
  def self.client(options = {})
12
37
  Sophos::Client.new(options)
13
38
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sophos_central_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janco Tanis
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-02-20 00:00:00.000000000 Z
10
+ date: 2025-03-21 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: faraday
@@ -94,7 +93,6 @@ dependencies:
94
93
  - - ">="
95
94
  - !ruby/object:Gem::Version
96
95
  version: '0'
97
- description:
98
96
  email: gems@jancology.com
99
97
  executables: []
100
98
  extensions: []
@@ -126,7 +124,6 @@ licenses:
126
124
  metadata:
127
125
  homepage_uri: https://rubygems.org/gems/sophos
128
126
  source_code_uri: https://github.com/jancotanis/sophos
129
- post_install_message:
130
127
  rdoc_options: []
131
128
  require_paths:
132
129
  - lib
@@ -141,8 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
138
  - !ruby/object:Gem::Version
142
139
  version: '0'
143
140
  requirements: []
144
- rubygems_version: 3.2.12
145
- signing_key:
141
+ rubygems_version: 3.6.2
146
142
  specification_version: 4
147
143
  summary: A Ruby wrapper for the Sophos Central REST APIs (readonly)
148
144
  test_files: []