authoritarian 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ Authoritarian - Twitter OAuth Manager
2
+ =====================================
3
+
4
+ ## DESCRIPTION
5
+
6
+ Authoritarian is a command line interface for managing Twitter authorizations to
7
+ your application. God forbid Twitter give us a simple way to manually add
8
+ test accounts to our application.
9
+
10
+ Authoritarian follows the rules of [Semantic Versioning](http://semver.org/).
11
+
12
+
13
+ ## INSTALL
14
+
15
+ To install Authoritarian, simply install the gem:
16
+
17
+ $ [sudo] gem install authoritarian
18
+
19
+
20
+ ## TWITTER
21
+
22
+ ### APPLICATIONS
23
+
24
+ To authorize using Twitter's API, start by registering your application in
25
+ authoritarian:
26
+
27
+ $ authoritarian add application
28
+ Name: My Cool App
29
+ Consumer Key: HJvxyS06ykAA5AxYruY2p
30
+ Consumer Secret: 4wftaMYXpylkveupjQ4YDDZlarS35UJHR1ZHHSfjto
31
+
32
+ You can find your Consumer Key and Consumer Secret for your application by
33
+ going to the [Twitter Developer Applications](https://dev.twitter.com/apps) site
34
+ and clicking on the application you'd like to add. The key and secret are listed
35
+ under `OAuth 1.0a Settings`.
36
+
37
+ You can view your registered applications:
38
+
39
+ $ authoritarian show applications
40
+ $ authoritarian show applications --all
41
+
42
+ The `--all` option shows the consumer key and secret for each application.
43
+
44
+ Finally, you can remove your application by running:
45
+
46
+ $ authoritarian show applications
47
+
48
+ You'll be presented with prompts to select the application you wish to remove.
49
+
50
+ ### USERS
51
+
52
+ Once you've registered a Twitter application with Authoritarian, you can
53
+ authorize individual Twitter users to your application from the command line.
54
+
55
+ To authorize a user, simply type:
56
+
57
+ $ authoritarian add user
58
+
59
+ And to show a list of all users, type:
60
+
61
+ $ authoritarian show users
62
+ $ authoritarian show users --all
63
+
64
+ The `--all` option will append the OAuth token and OAuth token secret for each
65
+ user registered to Authoritarian.
66
+
67
+ To remove a user from Authoritarian (but not deauthorize), type:
68
+
69
+ $ authoritarian remove user
70
+
71
+
72
+ ## CONTRIBUTE
73
+
74
+ Contributions to Authoritarian are welcome! There is no mailing list currently
75
+ but you can add a feature you're interested in building to the GitHub Issues
76
+ page to discuss it.
77
+
78
+ If you do submit a pull request, please make sure you add test coverage and
79
+ send your request from a git topic branch.
80
+
81
+ You can find the repository here:
82
+
83
+ http://github.com/benbjohnson/authoritarian
@@ -0,0 +1,273 @@
1
+ #!/usr/bin/ruby
2
+
3
+ dir = File.join(File.dirname(File.expand_path(__FILE__)), '..', 'lib')
4
+ $:.unshift(dir)
5
+
6
+ require 'rubygems'
7
+ require 'authoritarian'
8
+ require 'commander/import'
9
+ require 'terminal-table/import'
10
+
11
+ program :name, 'Authoritarian'
12
+ program :version, Authoritarian::VERSION
13
+ program :description, 'An OAuth command line authorization tool.'
14
+
15
+
16
+ ################################################################################
17
+ # Initialization
18
+ ################################################################################
19
+
20
+ # Setup DataMapper
21
+ DataMapper.setup(:default, "sqlite://#{File.expand_path('~/authoritarian.db')}")
22
+ DataMapper.auto_upgrade!
23
+
24
+ # Catch CTRL-C and exit cleanly
25
+ trap("INT") do
26
+ puts
27
+ exit()
28
+ end
29
+
30
+ ################################################################################
31
+ # Helper Methods
32
+ ################################################################################
33
+
34
+ def ask_application(explicit=false, message='Please choose an application')
35
+ applications = Application.all
36
+ if applications.length == 0
37
+ puts "There are no applications. Please run 'authoritarian add application'."
38
+ exit()
39
+ elsif applications.length == 1 && !explicit
40
+ return applications[0].id
41
+ else
42
+ puts ""
43
+ puts "Applications"
44
+ applications.each_index do |index|
45
+ puts "#{index+1}. #{applications[index].name}"
46
+ end
47
+ index = ask("#{message} [1-#{applications.length}]: ", Integer) do
48
+ |q| q.in = 1..applications.length
49
+ end
50
+ puts ""
51
+ return applications[index-1].id
52
+ end
53
+ end
54
+
55
+ def ask_user(application, explicit=false, message='Please choose a user')
56
+ users = application.users
57
+ if users.length == 0
58
+ puts "There are no users. Please run 'authoritarian add user'."
59
+ exit()
60
+ elsif users.length == 1 && !explicit
61
+ return users[0].id
62
+ else
63
+ puts "Users"
64
+ users.each_index do |index|
65
+ puts "#{index+1}. #{users[index].username}"
66
+ end
67
+ index = ask("#{message} [1-#{users.length}]: ", Integer) do
68
+ |q| q.in = 1..users.length
69
+ end
70
+ puts ""
71
+ return users[index-1].id
72
+ end
73
+ end
74
+
75
+
76
+ ################################################################################
77
+ # Application Commands
78
+ ################################################################################
79
+
80
+ # Add application
81
+ command :'add application' do |c|
82
+ c.syntax = 'authoritarian add application'
83
+ c.description = 'Adds an application to the database.'
84
+ c.when_called do|args, options|
85
+ application = Application.new(:type => 'twitter')
86
+ application.name = ask("Name: ")
87
+ application.consumer_key = ask("Consumer Key: ")
88
+ application.consumer_secret = ask("Consumer Secret: ")
89
+ application.save()
90
+ end
91
+ end
92
+
93
+ # Remove Application
94
+ command :'remove application' do |c|
95
+ c.syntax = 'authoritarian remove application [options]'
96
+ c.description = 'Removes a registered application.'
97
+ c.option '--app-id ID', String, 'The identifier for the authorizing application.'
98
+ c.when_called do|args, options|
99
+ # Retrieve the application
100
+ options.app_id ||= ask_application(true, 'Please choose an application to delete')
101
+ application = Application.get(options.app_id)
102
+ raise "Cannot find application #{options.app_id}" if application.nil?
103
+
104
+ # Delete application
105
+ application.destroy
106
+ end
107
+ end
108
+
109
+ # Show applications
110
+ command :'show applications' do |c|
111
+ c.syntax = 'authoritarian show applications'
112
+ c.description = 'Displays all registered applications registered.'
113
+ c.option '--all', 'Shows all application data.'
114
+ c.when_called do|args, options|
115
+ applications = Application.all
116
+
117
+ if applications.length == 0
118
+ say "There are no registered applications."
119
+ else
120
+ application_table = table do |t|
121
+ t.headings = 'ID', 'Name'
122
+
123
+ # Optionally add consumer info
124
+ if options.all
125
+ t.headings << 'Consumer Key'
126
+ t.headings << 'Consumer Secret'
127
+ end
128
+
129
+ applications.each do |application|
130
+ row = [
131
+ application.id,
132
+ application.name[0..20],
133
+ ]
134
+
135
+ # Optionally add consumer info
136
+ if options.all
137
+ row << application.consumer_key
138
+ row << application.consumer_secret
139
+ end
140
+
141
+ t << row
142
+ end
143
+ end
144
+
145
+ puts application_table
146
+ end
147
+ end
148
+ end
149
+
150
+ # Show application
151
+ command :'show application' do |c|
152
+ c.syntax = 'authoritarian show application [options]'
153
+ c.description = 'Displays details of a single application.'
154
+ c.option '--id NUM', String, 'The application identifier.'
155
+ c.when_called do|args, options|
156
+ id = options.id || ask("ID: ")
157
+ application = Application.get(id)
158
+
159
+ if application.nil?
160
+ say "The specified application does not exist."
161
+ else
162
+ puts "Name: #{application.name}"
163
+ puts "Consumer Key: #{application.consumer_key}"
164
+ puts "Consumer Secret: #{application.consumer_secret}"
165
+ puts ""
166
+ end
167
+ end
168
+ end
169
+
170
+
171
+ ################################################################################
172
+ # User Commands
173
+ ################################################################################
174
+
175
+ # Add User
176
+ command :'add user' do |c|
177
+ c.syntax = 'authoritarian authorize user [options]'
178
+ c.description = 'Authorizes a user to an application.'
179
+ c.option '--app-id ID', String, 'The identifier for the authorizing application.'
180
+ c.option '--username STRING', String, 'The username of the authorizing user.'
181
+ c.option '--password STRING', String, 'The password of the authorizing user.'
182
+ c.when_called do|args, options|
183
+ options.app_id ||= ask_application()
184
+
185
+ # Retrieve the application
186
+ application = Application.get(options.app_id)
187
+ raise "Cannot find application #{options.app_id}" if application.nil?
188
+
189
+ # Retrieve authorization token
190
+ options.username ||= ask("Username: ")
191
+ options.password ||= ask("Password: ") {|q| q.echo = '*'}
192
+ authorizer = Authoritarian::Twitter.new(
193
+ application.consumer_key,
194
+ application.consumer_secret
195
+ )
196
+ auth = authorizer.authorize(options.username, options.password)
197
+
198
+ # Create a user
199
+ user = User.new(:application => application)
200
+ user.username = options.username
201
+ user.oauth_token = auth[:token]
202
+ user.oauth_token_secret = auth[:secret]
203
+ user.save
204
+ end
205
+ end
206
+
207
+ # Remove User
208
+ command :'remove user' do |c|
209
+ c.syntax = 'authoritarian remove user [options]'
210
+ c.description = 'Removes a user from a registered application.'
211
+ c.option '--app-id ID', String, 'The identifier for the authorizing application.'
212
+ c.when_called do|args, options|
213
+ # Retrieve the application
214
+ options.app_id ||= ask_application()
215
+ application = Application.get(options.app_id)
216
+ raise "Cannot find application #{options.app_id}" if application.nil?
217
+
218
+ # Retrieve the user to remove
219
+ options.user_id ||= ask_user(application, true, 'Please choose a user to remove')
220
+ user = User.get(options.user_id)
221
+ raise "Cannot find user #{options.user_id}" if user.nil?
222
+
223
+ # Delete user
224
+ user.destroy
225
+ end
226
+ end
227
+
228
+ # Show authorized users
229
+ command :'show users' do |c|
230
+ c.syntax = 'authoritarian show users'
231
+ c.description = 'Displays all users authorized to registered applications.'
232
+ c.option '--app-id ID', String, 'The identifier for the authorizing application.'
233
+ c.option '--all', 'Shows all user data.'
234
+ c.when_called do|args, options|
235
+ options.app_id ||= ask_application()
236
+
237
+ # Retrieve the application
238
+ application = Application.get(options.app_id)
239
+ raise "Cannot find application #{options.app_id}" if application.nil?
240
+ users = application.users
241
+
242
+ if users.length == 0
243
+ say "There are no users authorized to use #{application.name}."
244
+ else
245
+ user_table = table do |t|
246
+ t.headings = 'ID', 'Username'
247
+
248
+ # Optionally add oauth info
249
+ if options.all
250
+ t.headings << 'OAuth Token'
251
+ t.headings << 'OAuth Token Secret'
252
+ end
253
+
254
+ users.each do |user|
255
+ row = [
256
+ user.id,
257
+ user.username,
258
+ ]
259
+
260
+ # Optionally add oauth info
261
+ if options.all
262
+ row << user.oauth_token
263
+ row << user.oauth_token_secret
264
+ end
265
+
266
+ t << row
267
+ end
268
+ end
269
+
270
+ puts user_table
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,16 @@
1
+ dir = File.dirname(File.expand_path(__FILE__))
2
+ $:.unshift(dir)
3
+
4
+ require 'oauth'
5
+ require 'mechanize'
6
+ require 'dm-core'
7
+ require 'dm-migrations'
8
+ require 'dm-timestamps'
9
+ require 'dm-validations'
10
+ require 'authoritarian/model'
11
+ require 'authoritarian/twitter'
12
+ require 'authoritarian/version'
13
+
14
+ module Authoritarian
15
+ class InvalidLoginError < StandardError; end
16
+ end
@@ -0,0 +1,9 @@
1
+ DataMapper::Model.raise_on_save_failure = true
2
+
3
+ DataMapper::Property::String.length(255)
4
+ DataMapper::Property::Boolean.allow_nil(false)
5
+
6
+ require 'authoritarian/model/application'
7
+ require 'authoritarian/model/user'
8
+
9
+ DataMapper.finalize
@@ -0,0 +1,16 @@
1
+ class Application
2
+ include ::DataMapper::Resource
3
+ has n, :users
4
+
5
+ property :id, Serial
6
+ property :type, String
7
+ property :name, String
8
+ property :consumer_key, String
9
+ property :consumer_secret, String
10
+ timestamps :at
11
+
12
+ validates_presence_of :type
13
+ validates_presence_of :name
14
+ validates_presence_of :consumer_key
15
+ validates_presence_of :consumer_secret
16
+ end
@@ -0,0 +1,12 @@
1
+ class User
2
+ include ::DataMapper::Resource
3
+ belongs_to :application
4
+
5
+ property :id, Serial
6
+ property :username, String
7
+ property :oauth_token, String
8
+ property :oauth_token_secret, String
9
+ timestamps :at
10
+
11
+ validates_presence_of :application_id
12
+ end
@@ -0,0 +1,103 @@
1
+ module Authoritarian
2
+ # This class performs OAuth authorization for Twitter user accounts.
3
+ class Twitter
4
+ ############################################################################
5
+ # Constructor
6
+ ############################################################################
7
+
8
+ # Creates a Twitter OAuth authorizer.
9
+ def initialize(consumer_key=nil, consumer_secret=nil)
10
+ self.consumer_key = consumer_key
11
+ self.consumer_secret = consumer_secret
12
+ end
13
+
14
+
15
+ ############################################################################
16
+ # Public Attributes
17
+ ############################################################################
18
+
19
+ # The consumer key for the application that is authorizing the user.
20
+ attr_accessor :consumer_key
21
+
22
+ # The consumer secret for the application that is authorizing the user.
23
+ attr_accessor :consumer_secret
24
+
25
+
26
+ ############################################################################
27
+ # Public Methods
28
+ ############################################################################
29
+
30
+ def authorize(username, password)
31
+ # Generate request token
32
+ request_token = consumer.get_request_token
33
+
34
+ # Create in-process browser
35
+ mechanize = Mechanize.new do |agent|
36
+ #agent.user_agent_alias = 'Authoritarian'
37
+ end
38
+
39
+ # Request authorization
40
+ mechanize.get(generate_authorize_url(request_token)) do |login_page|
41
+ # Login and allow
42
+ form = *login_page.forms
43
+ form.field_with(:name => 'session[username_or_email]').value = username
44
+ form.field_with(:name => 'session[password]').value = password
45
+ auth_page = form.submit(form.button_with(:value => 'Allow'))
46
+
47
+ # Verify returned page has no errors
48
+ error = auth_page.parser.css('p.oauth-errors').text.strip
49
+ if error != ''
50
+ raise Authoritarian::InvalidLoginError.new(error)
51
+ end
52
+
53
+ # If not then get access token from PIN
54
+ pin = auth_page.parser.css('div#oauth_pin').text.strip
55
+
56
+ # Create OAuth token from PIN
57
+ access_token = request_token.get_access_token(:oauth_verifier => pin)
58
+ return {:token => access_token.token, :secret => access_token.secret}
59
+ end
60
+
61
+ return nil
62
+ end
63
+
64
+
65
+ ############################################################################
66
+ # Private Methods
67
+ ############################################################################
68
+
69
+ protected
70
+
71
+ # Generates the authorization url to have the user log into.
72
+ def generate_authorize_url(request_token)
73
+ # Create request
74
+ request =
75
+ consumer.create_signed_request(
76
+ :get,
77
+ consumer.authorize_path,
78
+ request_token,
79
+ {:oauth_callback => 'oob'}
80
+ )
81
+
82
+ # Alter parameters
83
+ params = request['Authorization'].sub(/^OAuth\s+/, '').split(/,\s+/).map { |p|
84
+ k, v = p.split('=')
85
+ v =~ /"(.*?)"/
86
+ "#{k}=#{CGI::escape($1)}"
87
+ }.join('&')
88
+
89
+ # Return authorize url
90
+ return "https://api.twitter.com#{request.path}?#{params}"
91
+ end
92
+
93
+ # The OAuth consumer object.
94
+ def consumer
95
+ @consumer ||=
96
+ ::OAuth::Consumer.new(
97
+ consumer_key,
98
+ consumer_secret,
99
+ :site => "https://api.twitter.com"
100
+ )
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,3 @@
1
+ module Authoritarian
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,218 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: authoritarian
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Ben Johnson
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-02-28 00:00:00 -07:00
19
+ default_executable: authoritarian
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: mechanize
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 23
30
+ segments:
31
+ - 1
32
+ - 0
33
+ - 0
34
+ version: 1.0.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: data_mapper
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 19
46
+ segments:
47
+ - 1
48
+ - 0
49
+ - 2
50
+ version: 1.0.2
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: dm-sqlite-adapter
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 19
62
+ segments:
63
+ - 1
64
+ - 0
65
+ - 2
66
+ version: 1.0.2
67
+ type: :runtime
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: commander
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ hash: 57
78
+ segments:
79
+ - 4
80
+ - 0
81
+ - 3
82
+ version: 4.0.3
83
+ type: :runtime
84
+ version_requirements: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ name: terminal-table
87
+ prerelease: false
88
+ requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ hash: 3
94
+ segments:
95
+ - 1
96
+ - 4
97
+ - 2
98
+ version: 1.4.2
99
+ type: :runtime
100
+ version_requirements: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ name: rspec
103
+ prerelease: false
104
+ requirement: &id006 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ hash: 31
110
+ segments:
111
+ - 2
112
+ - 4
113
+ - 0
114
+ version: 2.4.0
115
+ type: :development
116
+ version_requirements: *id006
117
+ - !ruby/object:Gem::Dependency
118
+ name: mocha
119
+ prerelease: false
120
+ requirement: &id007 !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ hash: 35
126
+ segments:
127
+ - 0
128
+ - 9
129
+ - 12
130
+ version: 0.9.12
131
+ type: :development
132
+ version_requirements: *id007
133
+ - !ruby/object:Gem::Dependency
134
+ name: unindentable
135
+ prerelease: false
136
+ requirement: &id008 !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ hash: 27
142
+ segments:
143
+ - 0
144
+ - 1
145
+ - 0
146
+ version: 0.1.0
147
+ type: :development
148
+ version_requirements: *id008
149
+ - !ruby/object:Gem::Dependency
150
+ name: fakeweb
151
+ prerelease: false
152
+ requirement: &id009 !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ hash: 27
158
+ segments:
159
+ - 1
160
+ - 3
161
+ - 0
162
+ version: 1.3.0
163
+ type: :development
164
+ version_requirements: *id009
165
+ description:
166
+ email:
167
+ - benbjohnson@yahoo.com
168
+ executables:
169
+ - authoritarian
170
+ extensions: []
171
+
172
+ extra_rdoc_files: []
173
+
174
+ files:
175
+ - lib/authoritarian/model/application.rb
176
+ - lib/authoritarian/model/user.rb
177
+ - lib/authoritarian/model.rb
178
+ - lib/authoritarian/twitter.rb
179
+ - lib/authoritarian/version.rb
180
+ - lib/authoritarian.rb
181
+ - README.md
182
+ - bin/authoritarian
183
+ has_rdoc: true
184
+ homepage: http://github.com/benbjohnson/authoritarian
185
+ licenses: []
186
+
187
+ post_install_message:
188
+ rdoc_options: []
189
+
190
+ require_paths:
191
+ - lib
192
+ required_ruby_version: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ">="
196
+ - !ruby/object:Gem::Version
197
+ hash: 3
198
+ segments:
199
+ - 0
200
+ version: "0"
201
+ required_rubygems_version: !ruby/object:Gem::Requirement
202
+ none: false
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ hash: 3
207
+ segments:
208
+ - 0
209
+ version: "0"
210
+ requirements: []
211
+
212
+ rubyforge_project:
213
+ rubygems_version: 1.3.7
214
+ signing_key:
215
+ specification_version: 3
216
+ summary: A command line OAuth authorization tool.
217
+ test_files: []
218
+