pprof 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +9 -0
- data/README.md +168 -0
- data/bin/pprof +128 -0
- data/lib/pprof.rb +6 -0
- data/lib/pprof/entitlements.rb +93 -0
- data/lib/pprof/output_formatter.rb +119 -0
- data/lib/pprof/provisioning_profile.rb +103 -0
- data/lib/pprof/version.rb +3 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b298d026ed403f21ea18ce7023b6a31369b749c1
|
4
|
+
data.tar.gz: 9dbfec58d992aa3d80decc8bbb3f8167f2839a0a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4be4244290d618ceb0a91b23ef21d9d327dadee0840e4291ac8d854b8f2a137269099898fb8c1001f08863df9eeabd37609b58431cb1340a1e71110aad275c4a
|
7
|
+
data.tar.gz: f00597c5cf7be23cf250861d47c8ac3cb5e75a43b15440155733a405baf7b5e8a4603caf6489a5e601bea7c6338ee23aba632268c0b4652b1da13e1edde899c7
|
data/LICENSE
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
- MIT LICENSE -
|
2
|
+
|
3
|
+
Copyright (c) 2016 Olivier Halligon
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
# pprof
|
2
|
+
|
3
|
+
[![Twitter: @aligatr](https://img.shields.io/badge/contact-@aligatr-blue.svg?style=flat)](https://twitter.com/aligatr)
|
4
|
+
[![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/AliSoftware/pprof/blob/master/LICENSE)
|
5
|
+
|
6
|
+
`pprof` is a ruby library and binary to manipulate Provisioning Profiles.
|
7
|
+
|
8
|
+
It can help you create ruby scripts to list, get information, find and filter Provisioning Profiles easily.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
### Rubygems
|
13
|
+
|
14
|
+
As of now this library is very early stage and [hasn't been pushed on RubyGems yet](https://github.com/AliSoftware/pprof/issues/4).
|
15
|
+
I intend to push it as soon as [#2](https://github.com/AliSoftware/pprof/issues/4) (unit tests) and [#3](https://github.com/AliSoftware/pprof/issues/4) (CHANGELOG) are addressed.
|
16
|
+
|
17
|
+
### Build from source
|
18
|
+
|
19
|
+
* Clone the repository
|
20
|
+
* Build it using `gem build pprof.gemspec`
|
21
|
+
* Install it using `gem install pprof-0.3.1.gem`
|
22
|
+
|
23
|
+
## Example usages
|
24
|
+
|
25
|
+
* Find all the Provisioning Profiles that are attached to a given Team, or with a given AppID, or that will expire after a given date.
|
26
|
+
|
27
|
+
* List all your Provisioning Profiles and their inner information, like the provisioned device UDIDs, the list of certificates (with their associated subject/name), etc.
|
28
|
+
|
29
|
+
### Using it from the command line
|
30
|
+
|
31
|
+
```sh
|
32
|
+
# List all provisioning profiles
|
33
|
+
$ pprof
|
34
|
+
|
35
|
+
# Filter provisioning profiles by name
|
36
|
+
$ pprof --name foo # only ones containing 'foo', case sensitive
|
37
|
+
$ pprof --name /foo/i # only ones containing 'foo', case insensitive
|
38
|
+
$ pprof --name '/foo|bar/' # only ones containing 'foo' or 'bar'
|
39
|
+
$ pprof --name /^foo$/ # only the ones exactly matching 'foo'
|
40
|
+
|
41
|
+
# Filter by AppID
|
42
|
+
$ pprof --appid com.foo # only ones containing 'com.foo'
|
43
|
+
$ pprof --appid '/com\.(foo|bar)/' # only ones containing 'com.foo' or 'com.bar'
|
44
|
+
|
45
|
+
# List only provisioning profiles having push notifications
|
46
|
+
$ pprof --aps
|
47
|
+
$ pprof --aps development
|
48
|
+
$ pprof --aps production
|
49
|
+
|
50
|
+
# List only provisioning profiles being expired or not
|
51
|
+
$ pprof --exp
|
52
|
+
$ pprof --no-exp
|
53
|
+
|
54
|
+
# List only provisioning profiles containing provisioned devices
|
55
|
+
$ pprof --has-devices
|
56
|
+
|
57
|
+
# Combine filters
|
58
|
+
$ pprof --has-devices --aps --appid com.foo
|
59
|
+
```
|
60
|
+
```sh
|
61
|
+
# Print info for a given Provisioning Profile
|
62
|
+
$ pprof '12345678-ABCD-EF90-1234-567890ABCDEF'
|
63
|
+
|
64
|
+
# Print certificates in a given PP
|
65
|
+
$ pprof --certs '12345678-ABCD-EF90-1234-567890ABCDEF'
|
66
|
+
|
67
|
+
# Print devices in a given PP
|
68
|
+
$ pprof --devices '12345678-ABCD-EF90-1234-567890ABCDEF'
|
69
|
+
|
70
|
+
# Print all info on a given PP
|
71
|
+
$ pprof --certs --devices --info '12345678-ABCD-EF90-1234-567890ABCDEF'
|
72
|
+
$ pprof -cdi '12345678-ABCD-EF90-1234-567890ABCDEF'
|
73
|
+
```
|
74
|
+
|
75
|
+
### Using it in Ruby
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
require 'pprof'
|
79
|
+
# Load the Provisioning Profile
|
80
|
+
p = PProf::ProvisioningProfile.new('12345678-ABCD-EF90-1234-567890ABCDEF')
|
81
|
+
|
82
|
+
# Print various informations
|
83
|
+
puts p.name
|
84
|
+
puts p.team_name
|
85
|
+
puts p.entitlements.aps_environment
|
86
|
+
puts p.provisioned_devices.count
|
87
|
+
|
88
|
+
# Use an OutputFormatter to pretty-print the info
|
89
|
+
o = PProf::OutputFormatter.new
|
90
|
+
o.print_info(p)
|
91
|
+
|
92
|
+
# You can also print into any IO other than $stdout, like a File
|
93
|
+
File.open('certs.txt', 'w') do |file|
|
94
|
+
o2 = PProf::OutputFormatter.new(file)
|
95
|
+
o2.print_info(p, :certs => true)
|
96
|
+
end
|
97
|
+
|
98
|
+
# And you can easily loop on all provisioning profiles and manipulate each
|
99
|
+
dir = PProf::ProvisioningProfile::DEFAULT_DIR
|
100
|
+
Dir["#{dir}/*.mobileprovision"].each do |file|
|
101
|
+
p = PProf::ProvisioningProfile.new(file)
|
102
|
+
puts p.name
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
|
107
|
+
## Anatomy of a Provisioning Profile
|
108
|
+
|
109
|
+
Provisioning Profiles are in fact PKCS7 files which contain a plist payload.
|
110
|
+
|
111
|
+
That plist payload itself contains various data, including some textual information (Team Name, AppID, …), dates (expiration date, etc) but also X509 Certificates (`OpenSSL::X509::Certificate`).
|
112
|
+
|
113
|
+
<details>
|
114
|
+
<summary>Outline of the two main classes `ProvisioningProfile` and `Entitlements`</summary>
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
PProf::ProvisioningProfile
|
118
|
+
::DEFAULT_DIR
|
119
|
+
new(file) => PProf::ProvisioningProfile
|
120
|
+
to_hash => Hash<String, Any>
|
121
|
+
|
122
|
+
name => String
|
123
|
+
uuid => String
|
124
|
+
app_id_name => String
|
125
|
+
app_id_prefix => String
|
126
|
+
creation_date => DateTime
|
127
|
+
expiration_date => DateTime
|
128
|
+
ttl => Int
|
129
|
+
team_ids => Array<String>
|
130
|
+
team_name => String
|
131
|
+
developer_certificates => Array<OpenSSL::X509::Certificate>
|
132
|
+
entitlements => PProf::Entitlements
|
133
|
+
provisioned_devices => Array<String>
|
134
|
+
provisions_all_devices => Bool
|
135
|
+
|
136
|
+
PProf::Entitlements
|
137
|
+
new(dict) => PProf::Entitlements
|
138
|
+
to_hash => Hash<String, Any>
|
139
|
+
[](key) => Any
|
140
|
+
has_key?(key) => Bool
|
141
|
+
keys => Array<String>
|
142
|
+
|
143
|
+
keychain_access_groups => Array<String>
|
144
|
+
get_task_allow => Bool
|
145
|
+
app_id => String
|
146
|
+
team_id => String
|
147
|
+
aps_environment => String
|
148
|
+
app_groups => Array<String>
|
149
|
+
beta_reports_active => Bool
|
150
|
+
healthkit => Bool
|
151
|
+
ubiquity_container_identifiers => Array<String>
|
152
|
+
ubiquity_kvstore_identifier => String
|
153
|
+
```
|
154
|
+
</details>
|
155
|
+
|
156
|
+
## Contributing
|
157
|
+
|
158
|
+
There's plenty of room for improvement, including:
|
159
|
+
|
160
|
+
* Additional filters
|
161
|
+
* Ability to change the output format
|
162
|
+
* Parsing of additional entitlement keys
|
163
|
+
|
164
|
+
Don't hesitate to contribute, either with an Issue to give ideas or additional keys that aren't parsed yet, or via a Pull Request to provide new features yourself!
|
165
|
+
|
166
|
+
## License
|
167
|
+
|
168
|
+
This project is under the MIT license. See `LICENSE` file for more details.
|
data/bin/pprof
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pprof'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
|
7
|
+
def matcher(string)
|
8
|
+
m = string.match(%r[^/(.*)/(.*)$])
|
9
|
+
if m.nil?
|
10
|
+
Regexp.new(Regexp.escape(string))
|
11
|
+
else
|
12
|
+
Regexp.new(m[1], m[2].empty? ? nil : m[2])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Command Line Parsing
|
17
|
+
|
18
|
+
filters = {}
|
19
|
+
options = {}
|
20
|
+
parser = OptionParser.new do |opts|
|
21
|
+
opts.banner = <<-BANNER.gsub(/^ *\|/,'')
|
22
|
+
|Usage:
|
23
|
+
| pprof [options] [PATH|UUID]
|
24
|
+
| pprof [filters]
|
25
|
+
|
|
26
|
+
|Note: All filters expecting a string are interpreted as regular expressions if surrounded by slashes
|
27
|
+
BANNER
|
28
|
+
|
29
|
+
opts.separator ""
|
30
|
+
opts.separator "Print options"
|
31
|
+
|
32
|
+
opts.on("-i", "--info", "Print general info (default)") do
|
33
|
+
options[:info] = true
|
34
|
+
end
|
35
|
+
opts.on("-c", "--certs", "Print certificates") do
|
36
|
+
options[:certs] = true
|
37
|
+
end
|
38
|
+
opts.on("-d", "--devices", "Print provisioned devices") do
|
39
|
+
options[:devices] = true
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.separator ""
|
43
|
+
opts.separator "Filters"
|
44
|
+
|
45
|
+
opts.on("-n", "--name NAME", "Filter by name") do |name|
|
46
|
+
filters[:name] = matcher(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on("--appid-name APPID", "Filter by App ID Name") do |appid_name|
|
50
|
+
filters[:appid_name] = matcher(appid_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("-a", "--appid APPID", "Filter by App ID") do |appid|
|
54
|
+
filters[:appid] = matcher(appid)
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("--uuid UUID", "Filter by UUID") do |uuid|
|
58
|
+
filters[:uuid] = matcher(uuid)
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("-e", "--[no-]exp", "Only profiles (not) expired") do |flag|
|
62
|
+
filters[:exp] = flag
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on("--[no-]has-devices", "Filter by profiles having (no) provisioned devices") do |d|
|
66
|
+
filters[:has_devices] = d
|
67
|
+
end
|
68
|
+
|
69
|
+
opts.on("--[no-]all-devices", "Filter by profiles (not) provisioning all devices") do |d|
|
70
|
+
filters[:all_devices] = d
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on("--aps [ENV]", "Only profiles having Push entitlements (on a specific aps env)") do |env|
|
74
|
+
filters[:aps_env] = env.nil? ? true : matcher(env)
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
opts.separator ""
|
79
|
+
opts.separator "Misc"
|
80
|
+
|
81
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
82
|
+
puts opts
|
83
|
+
exit
|
84
|
+
end
|
85
|
+
|
86
|
+
opts.on_tail("-v", "--version", "Show version") do
|
87
|
+
puts PProf::VERSION
|
88
|
+
exit
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Parse the options, catching parse errors nicely
|
93
|
+
begin
|
94
|
+
parser.parse!
|
95
|
+
rescue OptionParser::InvalidOption => err
|
96
|
+
puts err
|
97
|
+
puts parser.help()
|
98
|
+
exit 1
|
99
|
+
end
|
100
|
+
|
101
|
+
# Don't mix filters and options together, that doesn't make sense
|
102
|
+
|
103
|
+
unless filters.empty? || ARGV.empty?
|
104
|
+
puts "You should use either filter flags to filter the whole list, or an specific path, not both."
|
105
|
+
puts parser # Usage
|
106
|
+
exit
|
107
|
+
end
|
108
|
+
|
109
|
+
unless options.empty? || !ARGV.empty?
|
110
|
+
puts "You should use option flags only when providing an specific path."
|
111
|
+
puts parser # Usage
|
112
|
+
exit
|
113
|
+
end
|
114
|
+
|
115
|
+
# Call the appropriate action
|
116
|
+
|
117
|
+
if ARGV.empty?
|
118
|
+
# Print list of matching profiles
|
119
|
+
o = PProf::OutputFormatter.new
|
120
|
+
o.print_filtered_list(PProf::ProvisioningProfile::DEFAULT_DIR, filters)
|
121
|
+
else
|
122
|
+
# Print info about given profile path/UUID
|
123
|
+
p = PProf::ProvisioningProfile.new(ARGV[0])
|
124
|
+
|
125
|
+
o = PProf::OutputFormatter.new
|
126
|
+
options = { :info => true } if options.empty?
|
127
|
+
o.print_info(p, options)
|
128
|
+
end
|
data/lib/pprof.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
module PProf
|
2
|
+
class Entitlements
|
3
|
+
def initialize(dict)
|
4
|
+
@dict = dict
|
5
|
+
end
|
6
|
+
|
7
|
+
# @return [Array<String>]
|
8
|
+
def keychain_access_groups
|
9
|
+
@dict['keychain-access-groups']
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [Bool]
|
13
|
+
def get_task_allow
|
14
|
+
@dict['get-task-allow']
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [String]
|
18
|
+
def app_id
|
19
|
+
@dict['application-identifier']
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String]
|
23
|
+
def team_id
|
24
|
+
@dict['com.apple.developer.team-identifier']
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String]
|
28
|
+
def aps_environment
|
29
|
+
@dict['aps-environment']
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Array<String>]
|
33
|
+
def app_groups
|
34
|
+
@dict['com.apple.security.application-groups']
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Bool]
|
38
|
+
def beta_reports_active
|
39
|
+
@dict['beta-reports-active']
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Bool]
|
43
|
+
def healthkit
|
44
|
+
@dict['com.apple.developer.healthkit']
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Array<String>]
|
48
|
+
def ubiquity_container_identifiers
|
49
|
+
@dict['com.apple.developer.ubiquity-container-identifiers']
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [String]
|
53
|
+
def ubiquity_kvstore_identifier
|
54
|
+
@dict['com.apple.developer.ubiquity-kvstore-identifier']
|
55
|
+
end
|
56
|
+
|
57
|
+
# Access entitlements by key
|
58
|
+
#
|
59
|
+
# @param [#to_s] key
|
60
|
+
# The entitlement key to read
|
61
|
+
#
|
62
|
+
def [](key)
|
63
|
+
@dict[key.to_s]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check if a given entitlement key is present
|
67
|
+
#
|
68
|
+
# @param [#to_s] key
|
69
|
+
# The key to check
|
70
|
+
#
|
71
|
+
def has_key?(key)
|
72
|
+
@dict.has_key?(key.to_s)
|
73
|
+
end
|
74
|
+
|
75
|
+
# The list of entitlement keys as String
|
76
|
+
#
|
77
|
+
# @return [Array<String>]
|
78
|
+
#
|
79
|
+
def keys
|
80
|
+
@dict.keys.map(&:to_s)
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_hash
|
84
|
+
@dict
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_s
|
88
|
+
@dict.map do |key, value|
|
89
|
+
"- #{key}: #{value}"
|
90
|
+
end.join("\n")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module PProf
|
2
|
+
class OutputFormatter
|
3
|
+
# Initialize a new OutputFormatter
|
4
|
+
#
|
5
|
+
# @param [IO] output
|
6
|
+
# The output destination where to print the formatted data.
|
7
|
+
# Defaults to $stdout
|
8
|
+
#
|
9
|
+
def initialize(output = $stdout)
|
10
|
+
@output = output
|
11
|
+
end
|
12
|
+
|
13
|
+
# A small helper to print ASCII tables
|
14
|
+
class ASCIITable
|
15
|
+
def initialize(*widths)
|
16
|
+
@widths = widths
|
17
|
+
end
|
18
|
+
|
19
|
+
def row(*cols)
|
20
|
+
'| ' + cols.zip(@widths).map do |c,w|
|
21
|
+
(c || '<nil>').to_s.ljust(w)[0...w]
|
22
|
+
end.join(' | ') + ' |'
|
23
|
+
end
|
24
|
+
|
25
|
+
def separator
|
26
|
+
'+' + @widths.map { |w| '-' * (w+2) }.join('+') + '+'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Prints the description of a Provisioning Profile
|
31
|
+
#
|
32
|
+
# @param [PProf::ProvisioningProfile] profile
|
33
|
+
# The ProvisioningProfile object to print
|
34
|
+
# @param [Hash<Symbol,Bool>] options
|
35
|
+
# Decide what to print. Valid keys are :info, :certs and :devices
|
36
|
+
#
|
37
|
+
def print_info(profile, options = nil)
|
38
|
+
options ||= { :info => true }
|
39
|
+
if options[:info]
|
40
|
+
keys = [:name, :uuid, :app_id_name, :app_id_prefix, :creation_date, :expiration_date, :ttl, :team_ids, :team_name]
|
41
|
+
keys.each do |key|
|
42
|
+
@output.puts "- #{key.to_s}: #{profile.send(key.to_sym)}"
|
43
|
+
end
|
44
|
+
@output.puts "- Entitlements:"
|
45
|
+
@output.puts profile.entitlements.to_s.split("\n").map { |line| " #{line}" }
|
46
|
+
end
|
47
|
+
|
48
|
+
if options[:info] || options[:certs]
|
49
|
+
@output.puts "- #{profile.developer_certificates.count} Developer Certificates"
|
50
|
+
profile.developer_certificates.each { |cert| @output.puts " - #{cert.subject}" } if options[:certs]
|
51
|
+
end
|
52
|
+
if options[:info] || options[:devices]
|
53
|
+
@output.puts "- #{(profile.provisioned_devices || []).count} Provisioned Devices"
|
54
|
+
profile.provisioned_devices.each { |udid| @output.puts " - #{udid}" } if options[:devices]
|
55
|
+
@output.puts "- Provision all devices: #{profile.provisions_all_devices.inspect}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Prints the filtered list of Provisioning Profiles
|
60
|
+
#
|
61
|
+
# Convenience method. Calls self.print_list with a filter block build from a filter hash
|
62
|
+
#
|
63
|
+
# @param [Hash<Symbol,Any>] filters
|
64
|
+
# The hash describing the applied filters
|
65
|
+
#
|
66
|
+
def print_filtered_list(dir = PProf::ProvisioningProfile::DEFAULT_DIR, filters = {})
|
67
|
+
print_list(dir) do |p|
|
68
|
+
(filters[:name].nil? || p.name =~ filters[:name]) &&
|
69
|
+
(filters[:appid_name].nil? || p.app_id_name =~ filters[:appid_name]) &&
|
70
|
+
(filters[:appid].nil? || p.entitlements.app_id =~ filters[:appid]) &&
|
71
|
+
(filters[:uuid].nil? || p.uuid =~ filters[:uuid]) &&
|
72
|
+
(filters[:exp].nil? || (p.expiration_date < DateTime.now) == filters[:exp]) &&
|
73
|
+
(filters[:has_devices].nil? || !(p.provisioned_devices || []).empty? == filters[:has_devices]) &&
|
74
|
+
(filters[:all_devices].nil? || p.provisions_all_devices == filters[:all_devices]) &&
|
75
|
+
(filters[:aps_env].nil? || match_aps_env(p.entitlements.aps_environment, filters[:aps_env])) &&
|
76
|
+
true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Prints the filtered list
|
81
|
+
#
|
82
|
+
# @param [String] dir
|
83
|
+
# The directory containing the mobileprovision files to list.
|
84
|
+
# Defaults to '~/Library/MobileDevice/Provisioning Profiles'
|
85
|
+
#
|
86
|
+
# @yield each provisioning provile for filtering/validation
|
87
|
+
# The block is given ProvisioningProfile object and should
|
88
|
+
# return true to display the row, false to filter it out
|
89
|
+
#
|
90
|
+
def print_list(dir = PProf::ProvisioningProfile::DEFAULT_DIR)
|
91
|
+
count = 0
|
92
|
+
|
93
|
+
table = PProf::OutputFormatter::ASCIITable.new(36, 60, 45, 25, 2, 10)
|
94
|
+
@output.puts table.separator
|
95
|
+
@output.puts table.row('UUID', 'Name', 'AppID', 'Expiration Date', ' ', 'Team Name')
|
96
|
+
@output.puts table.separator
|
97
|
+
|
98
|
+
Dir[dir + '/*.mobileprovision'].each do |file|
|
99
|
+
p = PProf::ProvisioningProfile.new(file)
|
100
|
+
|
101
|
+
next if block_given? && !yield(p)
|
102
|
+
|
103
|
+
state = DateTime.now < p.expiration_date ? "\u{2705}" : "\u{274c}" # 2705=checkmark, 274C=red X
|
104
|
+
@output.puts table.row(p.uuid, p.name, p.entitlements.app_id, p.expiration_date.to_time, state, p.team_name)
|
105
|
+
count += 1
|
106
|
+
end
|
107
|
+
|
108
|
+
@output.puts table.separator
|
109
|
+
@output.puts "#{count} Provisioning Profiles found."
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def self.match_aps_env(actual, expected)
|
114
|
+
return false if actual.nil? # false if no Push entitlements
|
115
|
+
return true if expected === true # true if Push present but we don't filter on specific env
|
116
|
+
return actual =~ expected # true if Push present and we filter on specific env
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'plist'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module PProf
|
6
|
+
class ProvisioningProfile
|
7
|
+
DEFAULT_DIR="#{ENV['HOME']}/Library/MobileDevice/Provisioning Profiles"
|
8
|
+
|
9
|
+
def initialize(file)
|
10
|
+
if file =~ %r/^[0-9A-F-]*$/i
|
11
|
+
path = "#{PProf::ProvisioningProfile::DEFAULT_DIR}/#{file}.mobileprovision"
|
12
|
+
else
|
13
|
+
path = file
|
14
|
+
end
|
15
|
+
pkcs7 = OpenSSL::PKCS7.new(File.read(path))
|
16
|
+
pkcs7.verify([], OpenSSL::X509::Store.new)
|
17
|
+
@plist = Plist::parse_xml(pkcs7.data)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String]
|
21
|
+
def name
|
22
|
+
@plist['Name']
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String]
|
26
|
+
def uuid
|
27
|
+
@plist['UUID']
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [String]
|
31
|
+
def app_id_name
|
32
|
+
@plist['AppIDName']
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [String]
|
36
|
+
def app_id_prefix
|
37
|
+
@plist['ApplicationIdentifierPrefix']
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [DateTime]
|
41
|
+
def creation_date
|
42
|
+
@plist['CreationDate']
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [DateTime]
|
46
|
+
def expiration_date
|
47
|
+
@plist['ExpirationDate']
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Int]
|
51
|
+
def ttl
|
52
|
+
@plist['TimeToLive'].to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [Array<String>]
|
56
|
+
def team_ids
|
57
|
+
@plist['TeamIdentifier']
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [String]
|
61
|
+
def team_name
|
62
|
+
@plist['TeamName']
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Array<OpenSSL::X509::Certificate>] List of certificates associated with this profile
|
66
|
+
def developer_certificates
|
67
|
+
@plist['DeveloperCertificates'].map do |data|
|
68
|
+
OpenSSL::X509::Certificate.new(data.string)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [Entitlements] All the entitlements associated with this profile
|
73
|
+
def entitlements
|
74
|
+
PProf::Entitlements.new(@plist['Entitlements'])
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Array<String>] List of provisioned devices if any
|
78
|
+
def provisioned_devices
|
79
|
+
@plist['ProvisionedDevices']
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Bool]
|
83
|
+
def provisions_all_devices
|
84
|
+
@plist['ProvisionsAllDevices'] || false
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_hash
|
88
|
+
@dict
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_s
|
92
|
+
lines = [:name, :uuid, :app_id_name, :app_id_prefix, :creation_date, :expiration_date, :ttl, :team_ids, :team_name].map do |key|
|
93
|
+
"- #{key.to_s}: #{self.send(key.to_sym)}"
|
94
|
+
end +
|
95
|
+
[
|
96
|
+
"- #{developer_certificates.count} Developer Certificates",
|
97
|
+
"- #{provisioned_devices.count} Provisioned Devices",
|
98
|
+
"- Entitlements:"
|
99
|
+
] + entitlements.to_hasg.map { |key, value| " - #{key}: #{value}" }
|
100
|
+
lines.join("\n")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pprof
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Olivier Halligon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-09-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: plist
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.1'
|
27
|
+
description: library and binary tool to manipulate Provisioning Profile files
|
28
|
+
email: olivier@halligon.net
|
29
|
+
executables:
|
30
|
+
- pprof
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- LICENSE
|
35
|
+
- README.md
|
36
|
+
- bin/pprof
|
37
|
+
- lib/pprof.rb
|
38
|
+
- lib/pprof/entitlements.rb
|
39
|
+
- lib/pprof/output_formatter.rb
|
40
|
+
- lib/pprof/provisioning_profile.rb
|
41
|
+
- lib/pprof/version.rb
|
42
|
+
homepage: http://rubygems.org/gems/pp
|
43
|
+
licenses:
|
44
|
+
- MIT
|
45
|
+
metadata: {}
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.0.0
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 2.6.7
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: A Provisioning Profiles library
|
66
|
+
test_files: []
|