apple_dep_client 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|