em-simple_telnet_server 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5404a8e42d38865f170a211f1abbce3ea34507a4
4
+ data.tar.gz: 70e576cbe3a4ea0cba867dd0a9f9d7563b5e3dd7
5
+ SHA512:
6
+ metadata.gz: 8dd5559a1bf310e77da155f67d0b83b09153f6d4ed4f7d7ed93cf7596c505d46c6d38262c077ab76cc4e67b3155f271bd0332b85c5741bc23829bbd628047fb1
7
+ data.tar.gz: 425b2379c7be3923c0d98d519618069abfe3954f15b269802a641d5291f1487d65b585a15e2cf0b53f2ff331a1105fdc8a582fc79a4f608f8a91a5aca43205e0
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in em-telnet_server.gemspec
4
+ gemspec
5
+
6
+ gem "em-simple_telnet", git: "https://github.com/paddor/em-simple_telnet.git"
data/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ Copyright (c) 2016, Patrik Wenger
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
+
5
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -0,0 +1,2 @@
1
+ README.md: README.gsl test/fake_machine.rb
2
+ gsl README
@@ -0,0 +1,58 @@
1
+ .output "README.md"
2
+ .template 1
3
+ [![Build Status on Travis CI](https://travis-ci.org/paddor/em-simple_telnet_server.svg?branch=master)](https://travis-ci.org/paddor/em-simple_telnet_server?branch=master)
4
+
5
+ # SimpleTelnetServer
6
+
7
+ This gem provides a simple way to implement your own telnet server. It's useful
8
+ for example if you want to mock a telnet server in your telnet-related
9
+ integration tests.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'em-simple_telnet_server'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install em-simple_telnet_server
26
+
27
+ ## Usage
28
+
29
+ Here's an example, right from [test/fake_machine.rb](https://github.com/paddor/em-simple_telnet_server/blob/master/test/fake_machine.rb).
30
+
31
+ ```ruby
32
+ .include "test/fake_machine.rb"
33
+ ```
34
+
35
+ You can run it as follows. By default, it'll start listening on "localhost" and port 10023.
36
+
37
+ ```ruby
38
+ EventMachine.run do
39
+ FakeMachine.start_server
40
+ end
41
+ ```
42
+
43
+ ## Development
44
+
45
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
46
+
47
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
48
+
49
+ ## Contributing
50
+
51
+ Bug reports and pull requests are welcome on GitHub at https://github.com/paddor/em-simple_telnet_server.
52
+
53
+
54
+ ## License
55
+
56
+ The gem is available as open source under the terms of the [ISC License](http://opensource.org/licenses/ISC).
57
+ See the [LICENSE](https://github.com/paddor/em-simple_telnet_server/blob/master/LICENSE) file.
58
+ .endtemplate
@@ -0,0 +1,141 @@
1
+ [![Build Status on Travis CI](https://travis-ci.org/paddor/em-simple_telnet_server.svg?branch=master)](https://travis-ci.org/paddor/em-simple_telnet_server?branch=master)
2
+
3
+ # SimpleTelnetServer
4
+
5
+ This gem provides a simple way to implement your own telnet server. It's useful
6
+ for example if you want to mock a telnet server in your telnet-related
7
+ integration tests.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'em-simple_telnet_server'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install em-simple_telnet_server
24
+
25
+ ## Usage
26
+
27
+ Here's an example, right from [test/fake_machine.rb](https://github.com/paddor/em-simple_telnet_server/blob/master/test/fake_machine.rb).
28
+
29
+ ```ruby
30
+ require 'em-simple_telnet_server'
31
+
32
+ ##
33
+ # This should just demonstrate what you can do with SimpleTelnetServer.
34
+ #
35
+ class FakeMachine < SimpleTelnetServer::Connection
36
+ # adds simple authentication and authorization
37
+ include SimpleTelnetServer::HasLogin
38
+
39
+ has_option :command_prompt, "fake$ "
40
+ has_option :login_prompt, "fakelogin: "
41
+ has_option :password_prompt, "fakepassword: "
42
+
43
+ has_login "fakeuser", "fakepass" # default is ":user" role
44
+ has_login "fakeroot", "fakerootpass", role: :admin
45
+
46
+ # Echo command.
47
+ has_command(/^s*echo (.*)/) do |what|
48
+ send_output(what) # this also sends a prompt back
49
+ end
50
+
51
+ # Send command prompt on return.
52
+ has_command(/^s*$/, :send_command_prompt)
53
+
54
+ # This is the callback that is called right after authorization. You could
55
+ # initialize your code here.
56
+ def on_authorization
57
+ send_output "Hello #{entered_username}! You're authorized now."
58
+ end
59
+
60
+ # Just to demonstrate the ability to use methods as command actions instead
61
+ # of blocks. This method will be called directly if the command "count up"
62
+ # is called. It also supports commands in the form of "count up 5".
63
+ #
64
+ # See the call to {.has_command} below.
65
+ def count_up(step)
66
+ step ||= 1
67
+ @count_up_number ||= 0
68
+ @count_up_number += step
69
+ send_output "The new number is #@count_up_number."
70
+ end
71
+ has_command("count up(?:s+(d+))", :count_up)
72
+
73
+ # Simulates a slow command which could be used to test timeouts.
74
+ # This can be invoked using "slow command".
75
+ def slow_command
76
+ EventMachine::Timer.new(3) { send_output "This is the output." }
77
+ end
78
+
79
+ # Recognizes commands like "sleep 3" and "sleep 1.5" and actually performs
80
+ # the sleep.
81
+ has_command(/^sleeps+(d+(?:.d+)?)s*$/) do |seconds|
82
+ EventMachine::Timer.new(seconds.to_f) do
83
+ send_output "This is the output."
84
+ end
85
+ end
86
+
87
+ # Simulates a command that ends in a very weird prompt.
88
+ #
89
+ # This is just to demonstrate the use of {#send_data}. It won't send the
90
+ # default command prompt like {#send_output} would.
91
+ has_command(/^weirds*$/) do
92
+ send_data("some
93
+ output
94
+ weird-prompt| ")
95
+ end
96
+
97
+ # logout
98
+ has_command(/^byes*$/, :close_connection)
99
+
100
+ # tanslate every command to a method call, if the method exists
101
+ # @note This is probably dangerous. But whatever, it's telnet.
102
+ has_command(/^([w ]+)$/) do |command|
103
+ method = command.gsub(/ /, '_')
104
+ if self.respond_to? method
105
+ self.send method
106
+ else
107
+ raise UnknownCommand, command
108
+ end
109
+ end
110
+
111
+ # Simplest test command ever. This will only be invokable because of the
112
+ # catch-all-and-translate-to-method-calls block above.
113
+ def foo
114
+ send_output("bar")
115
+ end
116
+ end
117
+ ```
118
+
119
+ You can run it as follows. By default, it'll start listening on "localhost" and port 10023.
120
+
121
+ ```ruby
122
+ EventMachine.run do
123
+ FakeMachine.start_server
124
+ end
125
+ ```
126
+
127
+ ## Development
128
+
129
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
130
+
131
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
132
+
133
+ ## Contributing
134
+
135
+ Bug reports and pull requests are welcome on GitHub at https://github.com/paddor/em-simple_telnet_server.
136
+
137
+
138
+ ## License
139
+
140
+ The gem is available as open source under the terms of the [ISC License](http://opensource.org/licenses/ISC).
141
+ See the [LICENSE](https://github.com/paddor/em-simple_telnet_server/blob/master/LICENSE) file.
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "em-simple_telnet_server"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'em-simple_telnet_server/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "em-simple_telnet_server"
8
+ spec.version = SimpleTelnetServer::VERSION
9
+ spec.authors = ["Patrik Wenger"]
10
+ spec.email = ["paddor@gmail.com"]
11
+
12
+ spec.summary = "Simple telnet server on EventMachine"
13
+ spec.description = "A simple way to implement your own telnet server on" +
14
+ " EventMachine"
15
+ spec.homepage = "http://github.com/paddor/em-simple_telnet_server"
16
+ spec.license = "ISC"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.11"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "minitest", "~> 5.0"
24
+ spec.add_development_dependency 'minitest-reporters'
25
+ spec.add_dependency('eventmachine', '>= 1.0.0')
26
+ end
@@ -0,0 +1,274 @@
1
+ require_relative "em-simple_telnet_server/version"
2
+ require_relative "em-simple_telnet_server/has_login"
3
+ require 'eventmachine'
4
+
5
+ # A basic Telnet server implemented with EventMachine.
6
+ class SimpleTelnetServer::Connection < EventMachine::Connection
7
+ # @return [Hash<Symbol, Object>] default values for (telnet) options
8
+ DEFAULT_OPTIONS = {
9
+ port: 10023,
10
+ command_prompt: "$ ",
11
+ login_prompt: "login: ",
12
+ password_prompt: "password: ",
13
+ }
14
+
15
+ class << self
16
+
17
+ # Starts the server on address _addr_ and port _port_.
18
+ def start_server(addr = 'localhost', port = options[:port])
19
+ EventMachine.start_server(addr, port, self)
20
+ end
21
+
22
+ # Sets the option _opt_ to value.
23
+ # @param opt [Symbol] option key
24
+ # @param value [Object] anything
25
+ def has_option(opt, value)
26
+ options[opt] = value
27
+ end
28
+
29
+ # Returns the (telnet) options for this class. If they are not initialized
30
+ # yet, the ones from superclass (or SimpleTelnetServer) are duplicated.
31
+ def options
32
+ @options ||= if top_class?
33
+ DEFAULT_OPTIONS
34
+ else
35
+ superclass.options
36
+ end.dup
37
+ end
38
+
39
+ # Registers an action for the command _cmd_ (which can be a Regexp, #===
40
+ # will be used). _action_ can be a Symbol referring to an instance method
41
+ # or an instance of Proc. If _action_ is not specified, the given block is
42
+ # used.
43
+ #
44
+ # If _cmd_ is a Regexp, all captures (MatchData#captures) will be passed
45
+ # to the block/method call (see {#run_command}).
46
+ #
47
+ # @param cmd [String, Regexp] fixed command or regular expression that
48
+ # matches a command and its arguments in capture groups
49
+ # @param action [Symbol] the method to call. if given, the block passed is
50
+ # ignored
51
+ def has_command(cmd, action = nil, &blk)
52
+ commands[cmd] = action || blk
53
+ end
54
+
55
+ # Returns the Hash of registered commands. If they are not initialized
56
+ # yet (@commands), the one from superclass is used. If we're TelnetServer
57
+ # itself, an empty Hash is used.
58
+ #
59
+ # @return [Hash{String, Regexp => Symbol, Proc}]
60
+ def commands
61
+ @commands ||= if top_class?
62
+ {}
63
+ else
64
+ superclass.commands.dup # copy from superclass
65
+ end
66
+ end
67
+
68
+ # @return [Boolean] whether we've reached the top of the relevant
69
+ # hieararchy
70
+ def top_class?
71
+ self == SimpleTelnetServer::Connection
72
+ end
73
+ end
74
+
75
+ # def send_data(data)
76
+ # warn "Server: >>> #{data.inspect}"
77
+ # super
78
+ # end
79
+
80
+
81
+ # custom handler of buffer content
82
+ # @return [Proc, #call] will be passed the content of the buffer
83
+ attr_accessor :custom_handler
84
+
85
+ # Called by EventMachine when a new connection attempt is made to this
86
+ # server (immediately after calling {#initialize}).
87
+ #
88
+ # Checks if {#needs_authentication?}, which returns +false+ if not
89
+ # overridden. If it authentication is needed, it'll initiate the login
90
+ # procedure (send login prompt, get username, get password, ...).
91
+ #
92
+ # Otherwise, any peer is authorized right away.
93
+ def post_init
94
+ @buffer = ""
95
+ if needs_authentication?
96
+ initiate_authentication
97
+ else
98
+ authorize # login anybody
99
+ end
100
+ end
101
+
102
+ # @abstract
103
+ # If authentication is not required, there won't be a login procedure and
104
+ # any peer is automatically logged in after connecting.
105
+ # @return [Boolean] whether this telnet server requires authentication
106
+ def needs_authentication?
107
+ false
108
+ end
109
+
110
+ # Called by EventMachine when new data is received. Appends _data_ to the
111
+ # buffer (@buffer). Calls {#process_buffer} if @buffer content ends with
112
+ # newline.
113
+ def receive_data data
114
+ # warn "Server: <<< #{data.inspect}"
115
+ @buffer << data
116
+
117
+ # work only with complete commands (ending with newline)
118
+ process_buffer if @buffer.end_with? "\n"
119
+ end
120
+
121
+ # @return [Boolean] whether the user is logged in
122
+ def authorized?
123
+ @connection_state == :authorized
124
+ end
125
+
126
+ # @abstract
127
+ # Called by EventMachine after the connection has been closed.
128
+ def unbind
129
+ end
130
+
131
+ # @abstract
132
+ # Called automatically when a received command is not known (no matching
133
+ # entry in @commands) and sends back an error message.
134
+ #
135
+ # @param command [String] the command that is not known
136
+ def command_not_known(command)
137
+ send_output "Command #{command.inspect} is not known."
138
+ end
139
+
140
+ # Returns the telnet options for this telnet server.
141
+ def options
142
+ self.class.options
143
+ end
144
+
145
+ # Returns the recognized commands for this telnet server.
146
+ def commands
147
+ self.class.commands
148
+ end
149
+
150
+ private
151
+
152
+ # Sends the command prompt.
153
+ def send_command_prompt
154
+ send_data options[:command_prompt]
155
+ end
156
+
157
+ # Sends _output_ and then the command prompt.
158
+ # Appens new line to output first, if it doesn't have one yet, unless it's
159
+ # the empty string.
160
+ # @param output [String] output to send before command prompt
161
+ def send_output(output)
162
+ output += "\n" unless output.end_with? "\n" or output.empty?
163
+ send_data output
164
+ send_command_prompt
165
+ end
166
+
167
+ # Processes the content of @buffer.
168
+ #
169
+ # If a {#custom_handler} is defined, it's called with the current buffer
170
+ # contents. The handler will be removed, so if it has to stay, it has to
171
+ # re-add itself.
172
+ #
173
+ # If the user is authorized, the commands in the buffer are executed.
174
+ #
175
+ # If the user isn't authorized, {#process_spam} is called, which does
176
+ # nothing by default.
177
+ #
178
+ # Ensures that the buffer is cleared.
179
+ def process_buffer
180
+ if handler = custom_handler
181
+ self.custom_handler = nil
182
+ handler.(@buffer)
183
+
184
+ elsif authorized?
185
+ run_commands
186
+ else
187
+ process_spam
188
+ end
189
+ ensure
190
+ @buffer.clear
191
+ end
192
+
193
+ # Authorizes the user. This is done by setting @connection_state to
194
+ # +:authorized+, calling the {#on_authorization} hook method, and sendng him
195
+ # the command prompt.
196
+ def authorize
197
+ @connection_state = :authorized
198
+ on_authorization
199
+ send_command_prompt
200
+ end
201
+
202
+ # @abstract
203
+ # Called right after authorization. You can override this method to
204
+ # initialize your code.
205
+ #
206
+ # Using {#initialize} instead is a bit clunky because it's expected to take
207
+ # EventMachine-specific arguments and happens before the user is authorized.
208
+ # Same goes for {#post_init}. For both you'd have to remember to call
209
+ # +super+.
210
+ def on_authorization
211
+ end
212
+
213
+ # Raised when a command has been received that doesn't match any registered
214
+ # command.
215
+ class UnknownCommand < RuntimeError
216
+ def initialize(command) @command = command end
217
+ attr_reader :command
218
+ end
219
+
220
+ # Runs the commands in the buffer. Will call {#run_command} for each command
221
+ # (line), no matter if it is recognized or not. If {UnknownCommand} is
222
+ # raised, it's handled using {#command_not_known}.
223
+ def run_commands
224
+ @buffer.lines.each do |command|
225
+ run_command(command.chomp)
226
+ end
227
+ rescue UnknownCommand
228
+ command_not_known $!.command
229
+ end
230
+
231
+ # Runs a command.
232
+ #
233
+ # Stores _command_ into @current_command for later use and looks it up. If
234
+ # it finds an action for it, executes the action.
235
+ #
236
+ # If the matching command pattern is a Regexp, the captures are passed to
237
+ # the action.
238
+ #
239
+ # @param command [String] command to run
240
+ # @raise [UnknownCommand] if the command is unknown
241
+ def run_command(command)
242
+ @current_command = command
243
+ if pair = commands.find { |pattern,| pattern === command }
244
+ pattern, action = pair
245
+ args = $~.captures if pattern.is_a? Regexp
246
+ execute_action(action, args) if action
247
+ else
248
+ raise UnknownCommand, command
249
+ end
250
+ end
251
+
252
+ # Invokes _action_ along with the given arguments.
253
+ #
254
+ # @param action [Proc, Symbol] code or a method on this server to call
255
+ # @param params [Array<Object>, nil] arguments for action (passed with splat
256
+ # operator)
257
+ # @raise [ArgumentError] if action is invalid
258
+ def execute_action(action, args = nil)
259
+ case action
260
+ when Proc
261
+ self.instance_exec(*args, &action)
262
+ when Symbol
263
+ self.send(action, *args)
264
+ else
265
+ raise ArgumentError, "invalid action #{action.inspect}"
266
+ end
267
+ end
268
+
269
+ # @abstract
270
+ # Called when data has been received while user isn't authorized. This could
271
+ # be used to {#close_connection}.
272
+ def process_spam
273
+ end
274
+ end
@@ -0,0 +1,140 @@
1
+ # Adds functionality for simple authentication and authorization.
2
+ #
3
+ # By default, login credentials are defined right in the class definition
4
+ # using {ClassMethods::has_login}. If something more dynamic is needed, just
5
+ # override {#authenticate}.
6
+ #
7
+ # After authentication, {#entered_username}, {#entered_password}, and
8
+ # {#authorized_role} are set.
9
+ module SimpleTelnetServer::HasLogin
10
+ # Extends klass with {ClassMethods}.
11
+ def self.included(klass)
12
+ klass.extend ClassMethods
13
+ end
14
+
15
+ module ClassMethods
16
+
17
+ # @return [Hash{login_type Symbol => Array<(username String, password
18
+ # String)>}] registered (read/write) login credentials
19
+ def login_credentials
20
+ options[:login_credentials] ||= {}
21
+ end
22
+
23
+ # Adds a pair of login credentials.
24
+ # @param username [String] username
25
+ # @param password [String] password
26
+ # @param role [Symbol] (:user) the associated role name
27
+ def has_login(username, password, role: :user)
28
+ login_credentials[role] = [ username, password ]
29
+ end
30
+ end
31
+
32
+ # @return [String] the entered username
33
+ # @note This doesn't necessarily mean the user is logged in. Use
34
+ # {#authorized?} to check for that.
35
+ attr_reader :entered_username
36
+
37
+ # @return [String] the password username
38
+ attr_reader :entered_password
39
+
40
+ # @return [Symbol] the associated role of the valid login credentials
41
+ attr_reader :authorized_role
42
+
43
+ # @return [true] true, as this module is about authentication
44
+ def needs_authentication?
45
+ true
46
+ end
47
+
48
+ # Initiates authentication. This means setting the connection state to
49
+ # +waiting_for_username+ and sending the login prompt.
50
+ def initiate_authentication
51
+ @connection_state = :waiting_for_username
52
+ send_login_prompt
53
+ end
54
+
55
+ # If the user was requested to enter his username, the username is read.
56
+ # If the user was requested to enter his password, the password is read.
57
+ #
58
+ # Otherwise, the normal (+super+) behavior proceeds.
59
+ #
60
+ # In all cases, it ensures that the buffer is cleared at the end.
61
+ #
62
+ def process_buffer
63
+ if waiting_for_username?
64
+ read_username_from_buffer
65
+
66
+ elsif waiting_for_password?
67
+ read_password_from_buffer
68
+
69
+ else
70
+ super
71
+ end
72
+ ensure
73
+ @buffer.clear
74
+ end
75
+
76
+ # Sends the login prompt.
77
+ def send_login_prompt
78
+ send_data options[:login_prompt]
79
+ end
80
+
81
+ # Sends the password prompt.
82
+ def send_password_prompt
83
+ send_data options[:password_prompt]
84
+ end
85
+
86
+ # @return [Boolean] whether we are waiting for the user to enter the username
87
+ def waiting_for_username?
88
+ @connection_state == :waiting_for_username
89
+ end
90
+
91
+ # @return [Boolean] whether we are waiting for the user to enter the password
92
+ def waiting_for_password?
93
+ @connection_state == :waiting_for_password
94
+ end
95
+
96
+ # Reads the password from buffer and authorizes the user
97
+ # if he can be authenticated. Otherwise the
98
+ # connection state is set back to +:waiting_for_username+ and the login
99
+ # prompt is sent.
100
+ def read_password_from_buffer
101
+ @entered_password = @buffer.chomp
102
+ if role = authenticate(@entered_username, @entered_password)
103
+ @authorized_role = role
104
+ authorize
105
+ else
106
+ @connection_state = :waiting_for_username
107
+ @entered_username = @entered_password = nil
108
+ send_login_failed
109
+ send_login_prompt
110
+ end
111
+ end
112
+
113
+ # Reads the username from buffer. Sends the password prompt
114
+ # afterwards ({#send_password_prompt}) and sets the connection state to
115
+ # +:waiting_for_password+.
116
+ def read_username_from_buffer
117
+ @entered_username = @buffer.strip
118
+ send_password_prompt
119
+ @connection_state = :waiting_for_password
120
+ end
121
+
122
+ # Sends the message "Sorry, please try again." and the login prompt.
123
+ def send_login_failed
124
+ send_data "Sorry, please try again.\n"
125
+ end
126
+
127
+
128
+ # Checks _user_ and _pass_ against all known login credentials.
129
+ #
130
+ # @param user [String] entered username
131
+ # @param pass [String] entered password
132
+ # @return [Symbol] associated role, if credentials are known
133
+ # @return [nil] if credentials are not known
134
+ def authenticate(user, pass)
135
+ self.class.login_credentials.each do |role, credentials|
136
+ return role if credentials == [ user, pass ]
137
+ end
138
+ return nil
139
+ end
140
+ end
@@ -0,0 +1,3 @@
1
+ module SimpleTelnetServer
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-simple_telnet_server
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Patrik Wenger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-reporters
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: eventmachine
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.0.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.0.0
83
+ description: A simple way to implement your own telnet server on EventMachine
84
+ email:
85
+ - paddor@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".travis.yml"
92
+ - Gemfile
93
+ - LICENSE
94
+ - Makefile
95
+ - README.gsl
96
+ - README.md
97
+ - Rakefile
98
+ - bin/console
99
+ - bin/setup
100
+ - em-simple_telnet_server.gemspec
101
+ - lib/em-simple_telnet_server.rb
102
+ - lib/em-simple_telnet_server/has_login.rb
103
+ - lib/em-simple_telnet_server/version.rb
104
+ homepage: http://github.com/paddor/em-simple_telnet_server
105
+ licenses:
106
+ - ISC
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.5.1
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Simple telnet server on EventMachine
128
+ test_files: []
129
+ has_rdoc: