pechkin 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1676e5e77d5898b67a9614012f5d982cb2c75c97847ba8fe22bfaf409988f78
4
- data.tar.gz: bd7a612a913ce4433ec8a4d78687f5eddb75b1cf28ca6675f33833b0ebc76955
3
+ metadata.gz: 59912e94ce5225522475cb9f77b3bbb83c398a5dada6b1f937c02c0a886e2e8f
4
+ data.tar.gz: dcf67693f3ecaa7de96ef00ee3e4bfc0347d6ee45f546a3c8e7ae9e5d39b34b0
5
5
  SHA512:
6
- metadata.gz: 806a857ddaf52ead16bbeec58bd1b9b4d3af8690011297fa35014058d3f0b1510ffa6694e4a651f33eab71814cae677b12d169df2c1a2fbd26eb57fd0f017169
7
- data.tar.gz: c699ddf5ad93cc7e5c9996fa398d734a5029fef470311c488cfc0d3df4d7124e8bb7b02498e7e69e27ce64d8109f5bab27f264642feefba5dc5a7ab6947877d1
6
+ metadata.gz: d071d69449005cf0dfae928a148391ea1247876bcc06cd52c588ca84d5210e37a66a0838e10511ed0a6554b1ecf6904961467e6d1fef916676a4296f34687367
7
+ data.tar.gz: d59c5fcbf1c97be064075bf012ca0d760d65edd911bd4cd551efcc64468292ed213423fcfb893823aa57191b38433bb803a09158cd3da2da18558f8a7bd5d795
@@ -3,6 +3,8 @@ require 'rack'
3
3
  require 'logger'
4
4
  require 'prometheus/middleware/collector'
5
5
  require 'prometheus/middleware/exporter'
6
+ require 'htauth'
7
+ require 'base64'
6
8
 
7
9
  require_relative 'pechkin/cli'
8
10
  require_relative 'pechkin/exceptions'
@@ -14,6 +16,8 @@ require_relative 'pechkin/connector_telegram'
14
16
  require_relative 'pechkin/channel'
15
17
  require_relative 'pechkin/configuration'
16
18
  require_relative 'pechkin/substitute'
19
+ require_relative 'pechkin/prometheus_utils'
20
+ require_relative 'pechkin/auth'
17
21
  require_relative 'pechkin/app'
18
22
 
19
23
  module Pechkin # :nodoc:
@@ -33,20 +37,24 @@ module Pechkin # :nodoc:
33
37
 
34
38
  def initialize(options)
35
39
  @options = options
36
- @configuration = Configuration.load_from_directory(options.config_file)
37
- @handler = Handler.new(@configuration.channels)
38
40
  end
39
41
 
40
42
  def run
43
+ if options.add_auth
44
+ add_auth
45
+ exit 0
46
+ end
47
+
48
+ @configuration = Configuration.load_from_directory(options.config_file)
49
+ @handler = Handler.new(@configuration.channels)
41
50
  configuration.list if options.list?
42
- exit 0 if options.check?
51
+ return if options.check?
43
52
 
44
53
  if options.send_data
45
54
  send_data
46
- exit 0
55
+ else
56
+ run_server
47
57
  end
48
-
49
- run_server
50
58
  end
51
59
 
52
60
  def run_server
@@ -79,5 +87,11 @@ module Pechkin # :nodoc:
79
87
  [m[1], m[2]]
80
88
  end
81
89
  end
90
+
91
+ def add_auth
92
+ user, password = options.add_auth.split(':')
93
+ Pechkin::Auth::Manager.new(options.htpasswd).add(user, password)
94
+ puts IO.read(options.htpasswd)
95
+ end
82
96
  end
83
97
  end
@@ -5,14 +5,19 @@ module Pechkin
5
5
  def build(handler, options)
6
6
  app = App.new
7
7
  app.handler = handler
8
-
8
+ prometheus = Pechkin::PrometheusUtils.registry
9
9
  logger = create_logger(options.log_dir)
10
10
 
11
11
  Rack::Builder.app do
12
12
  use Rack::CommonLogger, logger
13
13
  use Rack::Deflater
14
- use Prometheus::Middleware::Collector
15
- use Prometheus::Middleware::Exporter
14
+ use Prometheus::Middleware::Collector, registry: prometheus
15
+ # Add Auth check if found htpasswd file or it was excplicitly provided
16
+ # See CLI class for configuration details
17
+ if options.htpasswd
18
+ use Pechkin::Auth::Middleware, auth_file: options.htpasswd
19
+ end
20
+ use Prometheus::Middleware::Exporter, registry: prometheus
16
21
 
17
22
  run app
18
23
  end
@@ -0,0 +1,57 @@
1
+ module Pechkin
2
+ module Auth
3
+ # Utility class for altering htpasswd files
4
+ class Manager
5
+ attr_reader :htpasswd
6
+ def initialize(htpasswd)
7
+ @htpasswd = htpasswd
8
+ end
9
+
10
+ def add(user, password)
11
+ m = File.exist?(htpasswd) ? HTAuth::File::ALTER : HTAuth::File::CREATE
12
+ HTAuth::PasswdFile.open(htpasswd, m) do |f|
13
+ f.add_or_update(user, password, 'md5')
14
+ end
15
+ end
16
+ end
17
+
18
+ # Auth middleware to check if provided auth can be found in .htpasswd file
19
+ class Middleware
20
+ attr_reader :htpasswd
21
+
22
+ def initialize(app, auth_file:)
23
+ @htpasswd = HTAuth::PasswdFile.open(auth_file) if File.exist?(auth_file)
24
+ @app = app
25
+ end
26
+
27
+ def call(env)
28
+ if authorized?(env)
29
+ @app.call(env)
30
+ else
31
+ body = { status: 'error', reason: 'unathorized' }.to_json
32
+ ['401', { 'Content-Type' => 'application/json' }, [body]]
33
+ end
34
+ rescue StandardError => e
35
+ puts e.backtrace.reverse.join('\n\t')
36
+ body = { status: 'error', reason: e.message }.to_json
37
+ ['503', { 'Content-Type' => 'application/json' }, [body]]
38
+ end
39
+
40
+ private
41
+
42
+ def authorized?(env)
43
+ return true unless htpasswd
44
+
45
+ auth = env['HTTP_AUTHORIZATION'] || ''
46
+ auth.match(/^Basic (.+)$/) do |m|
47
+ check_auth(*Base64.decode64(m[1]).split(':'))
48
+ end
49
+ end
50
+
51
+ def check_auth(user, password)
52
+ e = htpasswd.fetch(user)
53
+ e && e.authenticated?(password)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -43,7 +43,7 @@ module Pechkin
43
43
  exit 2
44
44
  else
45
45
  parser.parse(args)
46
- values
46
+ new.post_init(values)
47
47
  end
48
48
  end
49
49
 
@@ -89,6 +89,9 @@ module Pechkin
89
89
 
90
90
  # Command Line Parser Builder
91
91
  class CLI
92
+ # Default file name for htpasswd file with auth credentials
93
+ PECHKIN_HTPASSWD_FILE = 'pechkin.htpasswd'.freeze
94
+
92
95
  extend CLIHelper
93
96
 
94
97
  separator 'Run options'
@@ -104,28 +107,49 @@ module Pechkin
104
107
 
105
108
  opt :log_dir, names: ['--log-dir [DIR]'],
106
109
  desc: 'Path to log directory. Output will be writen to' \
107
- 'pechkin.log file. If not specified will write to' \
110
+ 'pechkin.log file. If not specified will write to ' \
108
111
  'STDOUT'
112
+ opt :htpasswd, names: ['--auth-file FILE'],
113
+ desc: 'Path to .htpasswd file. By default ' \
114
+ '`pechkin.htpasswd` file will be looked up in ' \
115
+ 'configuration directory and if found then ' \
116
+ 'authorization will be enabled implicitly. ' \
117
+ 'Providing this option enables htpasswd based ' \
118
+ 'authorization explicitly. When making requests use ' \
119
+ 'Basic auth to authorize.'
109
120
 
110
121
  separator 'Utils for configuration maintenance'
111
-
112
122
  opt :list?, names: ['-l', '--[no-]list'],
113
123
  desc: 'List all endpoints'
114
124
 
115
125
  opt :check?, names: ['-k', '--[no-]check'],
116
126
  desc: 'Load configuration and exit'
117
127
  opt :send_data, names: ['-s', '--send ENDPOINT'],
118
- desc: 'Send data to specified ENDPOINT and exit. Requires' \
119
- '--data to be set.'
128
+ desc: 'Send data to specified ENDPOINT and exit. ' \
129
+ 'Requires --data to be set.'
120
130
  opt :preview, names: ['--preview'],
121
131
  desc: 'Print rendering result to STDOUT and exit. ' \
122
- 'Use with send'
132
+ 'Use with --send'
123
133
  opt :data, names: ['--data DATA'],
124
134
  desc: 'Data to send with --send flag. Json string or @filename.'
125
135
 
126
- separator 'Debug options'
136
+ separator 'Auth utils'
137
+ opt :add_auth, names: ['--add-auth USER:PASSWORD'],
138
+ desc: 'Add auth entry to .htpasswd file. By default ' \
139
+ 'pechkin.htpasswd from configuration directory ' \
140
+ 'will be used. Use --auth-file to specify other ' \
141
+ 'file to update. If file does not exist it will be ' \
142
+ 'created.'
127
143
 
144
+ separator 'Debug options'
128
145
  opt :debug?, names: ['--[no-]debug'],
129
146
  desc: 'Print debug information and stack trace on errors'
147
+
148
+ def post_init(values)
149
+ default_htpasswd = File.join(values.config_file, PECHKIN_HTPASSWD_FILE)
150
+ values.htpasswd ||= default_htpasswd
151
+
152
+ values
153
+ end
130
154
  end
131
155
  end
@@ -8,31 +8,14 @@ module Pechkin
8
8
 
9
9
  # Message template to render final message.
10
10
  class MessageTemplate
11
- class << self
12
- def ruby_v260_or_later?(ruby_version = RUBY_VERSION)
13
- # Need to compare two versions, one is 2.6.0 - which supports keyword
14
- # arguments in ERB#initialize. Everything below that should use legacy
15
- # arguments
16
- rb_v260 = [2, 6, 0]
17
- rb_current = ruby_version.split('.').map(&:to_i)
18
-
19
- rb_v260.zip(rb_current).each do |x, y|
20
- x ||= 0
21
- y ||= 0
22
- return false if y < x
23
- end
24
-
25
- true
26
- end
27
- end
28
-
29
- RUBY_V260 = MessageTemplate.ruby_v260_or_later?
11
+ ERB_INITIALIZE_KEYWORD_ARGUMENTS = ERB.instance_method(:initialize)
12
+ .parameters.assoc(:key)
30
13
 
31
14
  def initialize(erb)
32
15
  # ERB#initialize has different signature starting from Ruby 2.6.*
33
16
  # See link:
34
17
  # https://github.com/ruby/ruby/blob/2311087/NEWS#stdlib-updates-outstanding-ones-only
35
- if MessageTemplate::RUBY_V260
18
+ if MessageTemplate::ERB_INITIALIZE_KEYWORD_ARGUMENTS # Ruby 2.6+
36
19
  @erb_template = ERB.new(erb, trim_mode: '-')
37
20
  else
38
21
  safe_level = nil
@@ -0,0 +1,12 @@
1
+ module Pechkin
2
+ module PrometheusUtils # :nodoc:
3
+ class << self
4
+ def registry
5
+ registry = ::Prometheus::Client.registry
6
+ registry.gauge(:pechkin_start_time_seconds,
7
+ docstring: 'Startup timestamp').set(Time.now.to_i)
8
+ registry
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,7 +1,7 @@
1
1
  module Pechkin
2
2
  # Keeps actual version
3
3
  module Version
4
- VERSION = [1, 0, 3].freeze
4
+ VERSION = [1, 1, 0].freeze
5
5
  class << self
6
6
  def version_string
7
7
  VERSION.join('.')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pechkin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ilya Arkhanhelsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-11 00:00:00.000000000 Z
11
+ date: 2019-12-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: grape
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: htauth
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: prometheus-client
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +58,14 @@ dependencies:
44
58
  requirements:
45
59
  - - '='
46
60
  - !ruby/object:Gem::Version
47
- version: 2.0.6
61
+ version: 2.0.8
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - '='
53
67
  - !ruby/object:Gem::Version
54
- version: 2.0.6
68
+ version: 2.0.8
55
69
  description:
56
70
  email: ilya.arkhanhelsky at gmail.com
57
71
  executables:
@@ -62,6 +76,7 @@ files:
62
76
  - bin/pechkin
63
77
  - lib/pechkin.rb
64
78
  - lib/pechkin/app.rb
79
+ - lib/pechkin/auth.rb
65
80
  - lib/pechkin/channel.rb
66
81
  - lib/pechkin/cli.rb
67
82
  - lib/pechkin/configuration.rb
@@ -76,6 +91,7 @@ files:
76
91
  - lib/pechkin/exceptions.rb
77
92
  - lib/pechkin/handler.rb
78
93
  - lib/pechkin/message_template.rb
94
+ - lib/pechkin/prometheus_utils.rb
79
95
  - lib/pechkin/substitute.rb
80
96
  - lib/pechkin/version.rb
81
97
  homepage: https://github.com/iarkhanhelsky/pechkin