apple_dep_client 2.0.0 → 2.0.1
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 +4 -4
- data/.rubocop.yml +599 -0
- data/CHANGELOG.md +7 -0
- data/CONTRIBUTING.md +19 -0
- data/Gemfile +2 -2
- data/Rakefile +1 -1
- data/apple_dep_client.gemspec +6 -6
- data/example/example.rb +14 -14
- data/lib/apple_dep_client.rb +10 -10
- data/lib/apple_dep_client/account.rb +1 -1
- data/lib/apple_dep_client/auth.rb +12 -12
- data/lib/apple_dep_client/callback.rb +5 -6
- data/lib/apple_dep_client/configuration.rb +5 -5
- data/lib/apple_dep_client/device.rb +13 -13
- data/lib/apple_dep_client/error.rb +27 -29
- data/lib/apple_dep_client/hacks/typhoeus_request.rb +8 -8
- data/lib/apple_dep_client/profile.rb +9 -9
- data/lib/apple_dep_client/request.rb +7 -7
- data/lib/apple_dep_client/token.rb +11 -11
- data/lib/apple_dep_client/version.rb +1 -1
- data/spec/account_spec.rb +3 -3
- data/spec/auth_spec.rb +10 -10
- data/spec/callback_spec.rb +11 -11
- data/spec/configuration_spec.rb +15 -15
- data/spec/device_spec.rb +34 -34
- data/spec/error_spec.rb +10 -10
- data/spec/profile_spec.rb +8 -8
- data/spec/request_spec.rb +17 -17
- data/spec/spec_helper.rb +2 -2
- data/spec/token_spec.rb +20 -20
- data/spec/version_spec.rb +2 -2
- metadata +6 -4
data/CHANGELOG.md
CHANGED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Contributing to AppleDEPClient
|
2
|
+
|
3
|
+
Pull Requests and Issues are welcome for this repository.
|
4
|
+
|
5
|
+
## Setup
|
6
|
+
|
7
|
+
```shell
|
8
|
+
bundle install
|
9
|
+
```
|
10
|
+
|
11
|
+
## Tests
|
12
|
+
|
13
|
+
```shell
|
14
|
+
bundle exec rake
|
15
|
+
```
|
16
|
+
|
17
|
+
Please write spec tests in the appropriate file in https://github.com/cellabus/apple_dep_client/tree/master/spec
|
18
|
+
for any changes you write. You can also manually test
|
19
|
+
your changes by running the example scripts in https://github.com/cellabus/apple_dep_client/tree/master/example.
|
data/Gemfile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
source
|
1
|
+
source "https://rubygems.org"
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in apple_dep_client.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
# This isn't in the gemspec file because it is being read from github
|
7
|
-
gem
|
7
|
+
gem "plist", git: "https://github.com/cellabus/plist", ref: "3.1.2" # Used to handle responses in plist format
|
data/Rakefile
CHANGED
data/apple_dep_client.gemspec
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
lib = File.expand_path(
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
4
|
+
require "apple_dep_client/version"
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "apple_dep_client"
|
8
8
|
spec.version = AppleDEPClient::VERSION
|
9
|
-
spec.licenses = [
|
9
|
+
spec.licenses = ["MIT"]
|
10
10
|
spec.authors = ["Albert Wang"]
|
11
11
|
spec.email = ["albert@cellabus.com"]
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
12
|
+
spec.summary = "Client for Apple Device Enrollment Program"
|
13
|
+
spec.description = "This gem provides an easy way to authenticate and interact with Apple's Device Enrollment Program"
|
14
14
|
spec.homepage = "https://github.com/cellabus/apple_dep_client"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_dependency "oauth", "~> 0.4.7"
|
22
|
-
spec.add_dependency "typhoeus", "~> 0.7
|
22
|
+
spec.add_dependency "typhoeus", "~> 0.7"
|
23
23
|
spec.add_dependency "plist", "~> 3.1.0" # See Gemfile
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.3"
|
25
25
|
spec.add_development_dependency "rake", "~> 10"
|
data/example/example.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# This file is an example script prepopulated with data for depsim, Apple's
|
2
2
|
# DEP simulator
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require "apple_dep_client"
|
5
|
+
require "securerandom"
|
6
6
|
|
7
7
|
# depsim
|
8
8
|
AppleDEPClient.configure do |x|
|
9
|
-
x.apple_dep_server =
|
9
|
+
x.apple_dep_server = "http://localhost"
|
10
10
|
x.consumer_key = "CK_48dd68d198350f51258e885ce9a5c37ab7f98543c4a697323d75682a6c10a32501cb247e3db08105db868f73f2c972bdb6ae77112aea803b9219eb52689d42e6"
|
11
11
|
x.consumer_secret = "CS_34c7b2b531a600d99a0e4edcf4a78ded79b86ef318118c2f5bcfee1b011108c32d5302df801adbe29d446eb78f02b13144e323eb9aad51c79f01e50cb45c3a68"
|
12
12
|
x.access_token = "AT_927696831c59ba510cfe4ec1a69e5267c19881257d4bca2906a99d0785b785a6f6fdeb09774954fdd5e2d0ad952e3af52c6d8d2f21c924ba0caf4a031c158b89"
|
@@ -14,30 +14,30 @@ AppleDEPClient.configure do |x|
|
|
14
14
|
end
|
15
15
|
|
16
16
|
device_serials = []
|
17
|
-
p
|
17
|
+
p "Devices:"
|
18
18
|
AppleDEPClient::Device.fetch do |device|
|
19
19
|
p device
|
20
|
-
device_serials << device[
|
20
|
+
device_serials << device["serial_number"]
|
21
21
|
end
|
22
22
|
|
23
23
|
DEFAULT_DEP_PROFILE_DATA = {
|
24
|
-
profile_name:
|
25
|
-
url:
|
24
|
+
profile_name: "DEP Profile",
|
25
|
+
url: "http://localhost:5000/dep_checkin",
|
26
26
|
is_supervised: true,
|
27
27
|
is_mandatory: true,
|
28
28
|
is_mdm_removable: false,
|
29
|
-
support_phone_number:
|
30
|
-
support_email_address:
|
29
|
+
support_phone_number: "123-456-7890",
|
30
|
+
support_email_address: "support@cellabus.com",
|
31
31
|
org_magic: SecureRandom.uuid,
|
32
32
|
skip_setup_items: [:biometric, :siri],
|
33
|
-
department:
|
33
|
+
department: "Foo",
|
34
34
|
}
|
35
35
|
|
36
36
|
profile = AppleDEPClient::Profile.define(DEFAULT_DEP_PROFILE_DATA)
|
37
|
-
profile_uuid = profile[
|
38
|
-
p
|
37
|
+
profile_uuid = profile["profile_uuid"]
|
38
|
+
p "Profile UUID:"
|
39
39
|
p profile_uuid
|
40
|
-
p
|
40
|
+
p "Assigning profile to devices:"
|
41
41
|
p AppleDEPClient::Profile.assign(profile_uuid, device_serials)
|
42
|
-
p
|
42
|
+
p "Fetching profile info:"
|
43
43
|
p AppleDEPClient::Profile.fetch(profile_uuid)
|
data/lib/apple_dep_client.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "apple_dep_client/version"
|
2
|
+
require "apple_dep_client/error"
|
3
|
+
require "apple_dep_client/configuration"
|
4
4
|
|
5
5
|
module AppleDEPClient
|
6
6
|
extend Configuration
|
7
7
|
end
|
8
8
|
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
9
|
+
require "apple_dep_client/token"
|
10
|
+
require "apple_dep_client/auth"
|
11
|
+
require "apple_dep_client/callback"
|
12
12
|
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
13
|
+
require "apple_dep_client/request"
|
14
|
+
require "apple_dep_client/account"
|
15
|
+
require "apple_dep_client/device"
|
16
|
+
require "apple_dep_client/profile"
|
@@ -4,7 +4,7 @@ module AppleDEPClient
|
|
4
4
|
module Account
|
5
5
|
FETCH_PATH = "/account"
|
6
6
|
def self.fetch
|
7
|
-
AppleDEPClient::Request.make_request(AppleDEPClient::Request.make_url(FETCH_PATH), :get,
|
7
|
+
AppleDEPClient::Request.make_request(AppleDEPClient::Request.make_url(FETCH_PATH), :get, "")
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# Apple Session Authorization Token management
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require File.join(File.dirname(__FILE__),
|
6
|
-
require
|
7
|
-
require
|
3
|
+
require "json"
|
4
|
+
require "oauth"
|
5
|
+
require File.join(File.dirname(__FILE__), "hacks", "typhoeus_request")
|
6
|
+
require "securerandom"
|
7
|
+
require "typhoeus"
|
8
8
|
|
9
9
|
module AppleDEPClient
|
10
10
|
module Auth
|
@@ -14,16 +14,16 @@ module AppleDEPClient
|
|
14
14
|
OAUTH_PATH = "/session"
|
15
15
|
|
16
16
|
def self.get_session_token
|
17
|
-
options = {method: :get, headers: AppleDEPClient::Request::DEFAULT_HEADERS}
|
17
|
+
options = { method: :get, headers: AppleDEPClient::Request::DEFAULT_HEADERS }
|
18
18
|
request = Typhoeus::Request.new(AppleDEPClient::Request.make_url(OAUTH_PATH), options)
|
19
|
-
request.options[:headers].merge!({
|
19
|
+
request.options[:headers].merge!({ "Authorization" => oauth_header(request) })
|
20
20
|
request.run
|
21
21
|
response = request.response
|
22
|
-
AppleDEPClient::Error.check_request_error(response, auth:true)
|
22
|
+
AppleDEPClient::Error.check_request_error(response, auth: true)
|
23
23
|
parse_response response
|
24
24
|
end
|
25
25
|
|
26
|
-
def self.oauth_header
|
26
|
+
def self.oauth_header(request)
|
27
27
|
consumer = OAuth::Consumer.new(
|
28
28
|
AppleDEPClient.consumer_key,
|
29
29
|
AppleDEPClient.consumer_secret,
|
@@ -36,16 +36,16 @@ module AppleDEPClient
|
|
36
36
|
)
|
37
37
|
oauth_params = {
|
38
38
|
consumer: consumer,
|
39
|
-
realm:
|
39
|
+
realm: "ADM",
|
40
40
|
token: token,
|
41
41
|
}
|
42
42
|
oauth_helper = OAuth::Client::Helper.new request, oauth_params.merge(request_uri: AppleDEPClient::Request.make_url(OAUTH_PATH))
|
43
43
|
oauth_helper.header
|
44
44
|
end
|
45
45
|
|
46
|
-
def self.parse_response
|
46
|
+
def self.parse_response(response)
|
47
47
|
body = JSON.parse response.response_body
|
48
|
-
auth_session_token = body[
|
48
|
+
auth_session_token = body["auth_session_token"]
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
# Processing the device callback
|
2
2
|
|
3
|
-
require
|
3
|
+
require "plist"
|
4
4
|
|
5
5
|
module AppleDEPClient
|
6
6
|
module Callback
|
7
|
-
|
8
7
|
# Given an XML plist that is CMS-signed and DER encoded, return a ruby Hash
|
9
8
|
# of the data
|
10
9
|
def self.decode_callback(callback_data)
|
@@ -13,11 +12,11 @@ module AppleDEPClient
|
|
13
12
|
end
|
14
13
|
|
15
14
|
def self.decrypt_data(callback_data)
|
16
|
-
data = AppleDEPClient::Token.create_temp_file(
|
15
|
+
data = AppleDEPClient::Token.create_temp_file("data", callback_data, binary: true)
|
17
16
|
command = "openssl asn1parse -inform DER -in #{data.path}"
|
18
17
|
decrypted_data, errors = AppleDEPClient::Token.run_command command
|
19
18
|
AppleDEPClient::Token.remove_temp_file data
|
20
|
-
if decrypted_data ==
|
19
|
+
if decrypted_data == "" || errors != ""
|
21
20
|
raise AppleDEPClient::Error::CallbackError, "Incorrect data #{errors}"
|
22
21
|
end
|
23
22
|
remove_encryption_data(decrypted_data)
|
@@ -29,11 +28,11 @@ module AppleDEPClient
|
|
29
28
|
callback_data = callback_data.split
|
30
29
|
read = false
|
31
30
|
callback_data.select! do |line|
|
32
|
-
if line.include?
|
31
|
+
if line.include? "<!DOCTYPE"
|
33
32
|
read = true
|
34
33
|
end
|
35
34
|
read_line = read
|
36
|
-
if line.include?
|
35
|
+
if line.include? "</plist>"
|
37
36
|
read = false
|
38
37
|
end
|
39
38
|
read_line
|
@@ -11,15 +11,15 @@ module AppleDEPClient
|
|
11
11
|
access_token: nil,
|
12
12
|
access_secret: nil,
|
13
13
|
access_token_expiry: nil,
|
14
|
-
apple_dep_server:
|
15
|
-
user_agent:
|
14
|
+
apple_dep_server: "https://mdmenrollment.apple.com", # Domain that Apple's DEP servers are at
|
15
|
+
user_agent: "CellabusMDM",
|
16
16
|
}
|
17
17
|
|
18
18
|
DEP_CONFIG.freeze
|
19
19
|
|
20
20
|
attr_writer *DEP_CONFIG.keys
|
21
21
|
|
22
|
-
def method_missing(m, *
|
22
|
+
def method_missing(m, *_args, &_block)
|
23
23
|
if DEP_CONFIG.keys.include? m.to_sym
|
24
24
|
get_value(m)
|
25
25
|
else
|
@@ -28,9 +28,9 @@ module AppleDEPClient
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def get_value(m)
|
31
|
-
value =
|
31
|
+
value = instance_variable_get("@#{m}")
|
32
32
|
value = get_default_value(m) if value.nil?
|
33
|
-
(value.is_a? Proc)? value.call : value
|
33
|
+
(value.is_a? Proc) ? value.call : value
|
34
34
|
end
|
35
35
|
|
36
36
|
def get_default_value(key)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Methods for getting and setting device details
|
2
2
|
|
3
|
-
require
|
3
|
+
require "json"
|
4
4
|
|
5
5
|
module AppleDEPClient
|
6
6
|
module Device
|
@@ -12,23 +12,23 @@ module AppleDEPClient
|
|
12
12
|
|
13
13
|
def self.fetch(cursor: nil, url: nil)
|
14
14
|
url ||= FETCH_PATH
|
15
|
-
response = {
|
16
|
-
while response[
|
17
|
-
response = make_fetch_request response[
|
18
|
-
response[
|
15
|
+
response = { "cursor" => cursor, "more_to_follow" => "true" }
|
16
|
+
while response["more_to_follow"] == "true"
|
17
|
+
response = make_fetch_request response["cursor"], url
|
18
|
+
response["devices"].each do |device|
|
19
19
|
yield device
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
22
|
+
response["cursor"]
|
23
23
|
end
|
24
24
|
|
25
|
-
def self.make_fetch_request
|
25
|
+
def self.make_fetch_request(cursor, url)
|
26
26
|
body = fetch_body(cursor)
|
27
27
|
AppleDEPClient::Request.make_request(AppleDEPClient::Request.make_url(url), :post, body)
|
28
28
|
end
|
29
29
|
|
30
|
-
def self.fetch_body
|
31
|
-
body = {"limit" => FETCH_LIMIT}
|
30
|
+
def self.fetch_body(cursor)
|
31
|
+
body = { "limit" => FETCH_LIMIT }
|
32
32
|
if cursor
|
33
33
|
body["cursor"] = cursor
|
34
34
|
end
|
@@ -36,24 +36,24 @@ module AppleDEPClient
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def self.sync(cursor, &block)
|
39
|
-
|
39
|
+
fetch(cursor: cursor, url: SYNC_PATH, &block)
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.details(devices)
|
43
43
|
body = devices
|
44
44
|
body = JSON.dump body
|
45
45
|
response = AppleDEPClient::Request.make_request(AppleDEPClient::Request.make_url(DETAILS_PATH), :post, body)
|
46
|
-
response[
|
46
|
+
response["devices"]
|
47
47
|
end
|
48
48
|
|
49
49
|
# Accepts an array of device ID strings
|
50
50
|
# WARNING - this will remove devices from DEP accounts and
|
51
51
|
# may render devices permanently inoperable
|
52
52
|
def self.disown(devices)
|
53
|
-
body = {
|
53
|
+
body = { "devices" => devices }
|
54
54
|
body = JSON.dump body
|
55
55
|
response = AppleDEPClient::Request.make_request(AppleDEPClient::Request.make_url(DISOWN_PATH), :post, body)
|
56
|
-
response[
|
56
|
+
response["devices"]
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
@@ -2,52 +2,51 @@
|
|
2
2
|
|
3
3
|
module AppleDEPClient
|
4
4
|
module Error
|
5
|
-
|
6
5
|
AUTH_ERRORS = [
|
7
6
|
# Used by AppleDEPClient::Auth
|
8
|
-
["BadRequest", 400,
|
9
|
-
["Unauthorized", 401,
|
10
|
-
["Forbidden", 403,
|
7
|
+
["BadRequest", 400, ""],
|
8
|
+
["Unauthorized", 401, ""],
|
9
|
+
["Forbidden", 403, ""],
|
11
10
|
]
|
12
11
|
|
13
12
|
ERRORS = [
|
14
13
|
# Used by AppleDEPClient::Request
|
15
|
-
["MalformedRequest", 400,
|
16
|
-
["Unauthorized", 401,
|
17
|
-
["Forbidden", 403,
|
18
|
-
["MethodNotAllowed", 405,
|
14
|
+
["MalformedRequest", 400, "MALFORMED_REQUEST_BODY"],
|
15
|
+
["Unauthorized", 401, "UNAUTHORIZED"],
|
16
|
+
["Forbidden", 403, "FORBIDDEN"],
|
17
|
+
["MethodNotAllowed", 405, ""],
|
19
18
|
|
20
19
|
# Used by AppleDEPClient::Device
|
21
|
-
["InvalidCursor", 400,
|
22
|
-
["ExhaustedCursor", 400,
|
23
|
-
["CursorRequired", 400,
|
24
|
-
["ExpiredCursor", 400,
|
25
|
-
["NotFound", 200,
|
26
|
-
["DeviceIDRequired", 400,
|
20
|
+
["InvalidCursor", 400, "INVALID_CURSOR"],
|
21
|
+
["ExhaustedCursor", 400, "EXHAUSTED_CURSOR"],
|
22
|
+
["CursorRequired", 400, "CURSOR_REQUIRED"],
|
23
|
+
["ExpiredCursor", 400, "EXPIRED_CURSOR"],
|
24
|
+
["NotFound", 200, "NOT_FOUND"],
|
25
|
+
["DeviceIDRequired", 400, "DEVICE_ID_REQUIRED"],
|
27
26
|
|
28
27
|
# Used by AppleDEPClient::Profile
|
29
|
-
["ConfigUrlRequired", 400,
|
30
|
-
["ConfigNameRequired", 400,
|
31
|
-
["FlagsInvalid", 400,
|
32
|
-
["ConfigUrlInvalid", 400,
|
33
|
-
["ConfigNameInvalid", 400,
|
34
|
-
["DepartmentInvalid", 400,
|
35
|
-
["SupportPhoneInvalid", 400,
|
36
|
-
["SupportEmailInvalid", 400,
|
37
|
-
["DescriptionInvalid", 400,
|
38
|
-
["MagicInvalid", 400,
|
39
|
-
["ProfileUUIDRequired", 400,
|
40
|
-
["ProfileNotFound", 404,
|
28
|
+
["ConfigUrlRequired", 400, "CONFIG_URL_REQUIRED"],
|
29
|
+
["ConfigNameRequired", 400, "CONFIG_NAME_REQUIRED"],
|
30
|
+
["FlagsInvalid", 400, "FLAGS_INVALID"],
|
31
|
+
["ConfigUrlInvalid", 400, "CONFIG_URL_INVALID"],
|
32
|
+
["ConfigNameInvalid", 400, "CONFIG_NAME_INVALID"],
|
33
|
+
["DepartmentInvalid", 400, "DEPARTMENT_INVALID"],
|
34
|
+
["SupportPhoneInvalid", 400, "SUPPORT_PHONE_INVALID"],
|
35
|
+
["SupportEmailInvalid", 400, "SUPPORT_EMAIL_INVALID"],
|
36
|
+
["DescriptionInvalid", 400, "DESCRIPTION_INVALID"],
|
37
|
+
["MagicInvalid", 400, "MAGIC_INVALID"],
|
38
|
+
["ProfileUUIDRequired", 400, "PROFILE_UUID_REQUIRED"],
|
39
|
+
["ProfileNotFound", 404, "PROFILE_NOT_FOUND"],
|
41
40
|
]
|
42
41
|
|
43
42
|
def self.check_request_error(response, auth:false)
|
44
|
-
get_errors(auth:auth).each do |error_name, response_code, body|
|
43
|
+
get_errors(auth: auth).each do |error_name, response_code, body|
|
45
44
|
if response.code == response_code && response.body.include?(body)
|
46
45
|
raise RequestError, error_name
|
47
46
|
end
|
48
47
|
end
|
49
48
|
if response.code != 200
|
50
|
-
raise RequestError,
|
49
|
+
raise RequestError, "GenericError"
|
51
50
|
end
|
52
51
|
end
|
53
52
|
|
@@ -63,6 +62,5 @@ module AppleDEPClient
|
|
63
62
|
|
64
63
|
class CallbackError < RuntimeError
|
65
64
|
end
|
66
|
-
|
67
65
|
end
|
68
66
|
end
|