shopify-cli 1.3.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +2 -2
  3. data/.github/CONTRIBUTING.md +9 -1
  4. data/.github/PULL_REQUEST_TEMPLATE.md +10 -1
  5. data/.github/workflows/release.yml +61 -0
  6. data/.github/workflows/triage.yml +22 -0
  7. data/.gitignore +0 -1
  8. data/.rubocop.yml +61 -8
  9. data/.rubocop_todo.yml +11 -0
  10. data/.travis.yml +1 -0
  11. data/CHANGELOG.md +30 -0
  12. data/Gemfile +3 -2
  13. data/Gemfile.lock +39 -37
  14. data/README.md +39 -7
  15. data/RELEASING.md +19 -29
  16. data/Rakefile +2 -0
  17. data/dev.yml +2 -2
  18. data/docs/_config.yml +1 -18
  19. data/docs/app/node/commands/index.md +2 -80
  20. data/docs/app/node/index.md +2 -33
  21. data/docs/app/rails/commands/index.md +2 -78
  22. data/docs/app/rails/index.md +2 -34
  23. data/docs/core/index.md +2 -84
  24. data/docs/getting-started/index.md +2 -25
  25. data/docs/getting-started/install/index.md +1 -118
  26. data/docs/getting-started/migrate/index.md +2 -94
  27. data/docs/getting-started/uninstall/index.md +2 -35
  28. data/docs/getting-started/upgrade/index.md +2 -39
  29. data/docs/help/start-app/index.md +2 -4
  30. data/docs/index.md +2 -24
  31. data/install.sh +1 -1
  32. data/lib/project_types/extension/cli.rb +21 -11
  33. data/lib/project_types/extension/commands/extension_command.rb +2 -2
  34. data/lib/project_types/extension/features/argo.rb +117 -0
  35. data/lib/project_types/extension/forms/create.rb +2 -2
  36. data/lib/project_types/extension/models/specification.rb +35 -0
  37. data/lib/project_types/extension/models/specification_handlers/checkout_post_purchase.rb +19 -0
  38. data/lib/project_types/extension/models/specification_handlers/default.rb +67 -0
  39. data/lib/project_types/extension/models/specifications.rb +77 -0
  40. data/lib/project_types/extension/tasks/configure_features.rb +52 -0
  41. data/lib/project_types/extension/tasks/fetch_specifications.rb +38 -0
  42. data/lib/project_types/node/cli.rb +4 -1
  43. data/lib/project_types/node/commands/connect.rb +15 -0
  44. data/lib/project_types/node/commands/create.rb +10 -4
  45. data/lib/project_types/node/commands/generate.rb +2 -11
  46. data/lib/project_types/node/messages/messages.rb +16 -50
  47. data/lib/project_types/rails/cli.rb +4 -1
  48. data/lib/project_types/rails/commands/connect.rb +15 -0
  49. data/lib/project_types/rails/commands/create.rb +15 -12
  50. data/lib/project_types/rails/forms/create.rb +1 -1
  51. data/lib/project_types/rails/gem.rb +1 -1
  52. data/lib/project_types/rails/messages/messages.rb +8 -5
  53. data/lib/project_types/script/cli.rb +9 -5
  54. data/lib/project_types/script/commands/create.rb +6 -4
  55. data/lib/project_types/script/commands/enable.rb +12 -4
  56. data/lib/project_types/script/commands/push.rb +5 -13
  57. data/lib/project_types/script/config/extension_points.yml +17 -12
  58. data/lib/project_types/script/errors.rb +21 -0
  59. data/lib/project_types/script/forms/create.rb +26 -2
  60. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +10 -1
  61. data/lib/project_types/script/layers/application/build_script.rb +18 -17
  62. data/lib/project_types/script/layers/application/create_script.rb +12 -10
  63. data/lib/project_types/script/layers/application/extension_points.rb +24 -0
  64. data/lib/project_types/script/layers/application/push_script.rb +18 -16
  65. data/lib/project_types/script/layers/domain/errors.rb +7 -0
  66. data/lib/project_types/script/layers/domain/extension_point.rb +62 -7
  67. data/lib/project_types/script/layers/domain/metadata.rb +55 -0
  68. data/lib/project_types/script/layers/domain/push_package.rb +25 -6
  69. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +17 -52
  70. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +42 -11
  71. data/lib/project_types/script/layers/infrastructure/errors.rb +16 -0
  72. data/lib/project_types/script/layers/infrastructure/extension_point_repository.rb +10 -4
  73. data/lib/project_types/script/layers/infrastructure/project_creator.rb +2 -1
  74. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +25 -13
  75. data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +72 -0
  76. data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +59 -0
  77. data/lib/project_types/script/layers/infrastructure/script_service.rb +9 -1
  78. data/lib/project_types/script/layers/infrastructure/task_runner.rb +4 -3
  79. data/lib/project_types/script/messages/messages.rb +55 -4
  80. data/lib/project_types/script/script_project.rb +25 -16
  81. data/lib/project_types/script/ui/error_handler.rb +59 -1
  82. data/lib/project_types/theme/cli.rb +40 -0
  83. data/lib/project_types/theme/commands/connect.rb +54 -0
  84. data/lib/project_types/theme/commands/create.rb +48 -0
  85. data/lib/project_types/theme/commands/deploy.rb +38 -0
  86. data/lib/project_types/theme/commands/generate.rb +20 -0
  87. data/lib/project_types/theme/commands/generate/env.rb +79 -0
  88. data/lib/project_types/theme/commands/push.rb +55 -0
  89. data/lib/project_types/theme/commands/serve.rb +31 -0
  90. data/lib/project_types/theme/forms/connect.rb +34 -0
  91. data/lib/project_types/theme/forms/create.rb +22 -0
  92. data/lib/project_types/theme/messages/messages.rb +147 -0
  93. data/lib/project_types/theme/tasks/ensure_themekit_installed.rb +78 -0
  94. data/lib/project_types/theme/themekit.rb +113 -0
  95. data/lib/shopify-cli/admin_api.rb +42 -2
  96. data/lib/shopify-cli/api.rb +34 -33
  97. data/lib/shopify-cli/commands/config.rb +24 -0
  98. data/lib/shopify-cli/commands/connect.rb +32 -15
  99. data/lib/shopify-cli/commands/system.rb +10 -1
  100. data/lib/shopify-cli/context.rb +23 -2
  101. data/lib/shopify-cli/core/entry_point.rb +1 -1
  102. data/lib/shopify-cli/core/monorail.rb +6 -4
  103. data/lib/shopify-cli/feature.rb +0 -2
  104. data/lib/shopify-cli/http_request.rb +27 -0
  105. data/lib/shopify-cli/js_deps.rb +1 -1
  106. data/lib/shopify-cli/messages/messages.rb +31 -7
  107. data/lib/shopify-cli/method_object.rb +104 -0
  108. data/lib/shopify-cli/partners_api.rb +25 -3
  109. data/lib/shopify-cli/process_supervision.rb +1 -1
  110. data/lib/shopify-cli/project.rb +12 -8
  111. data/lib/shopify-cli/project_type.rb +18 -2
  112. data/lib/shopify-cli/resolve_constant.rb +25 -0
  113. data/lib/shopify-cli/result.rb +432 -0
  114. data/lib/shopify-cli/shopifolk.rb +87 -0
  115. data/lib/shopify-cli/task.rb +8 -0
  116. data/lib/shopify-cli/tasks/create_api_client.rb +13 -2
  117. data/lib/shopify-cli/tasks/ensure_env.rb +3 -0
  118. data/lib/shopify-cli/tasks/select_org_and_shop.rb +10 -5
  119. data/lib/shopify-cli/tunnel.rb +8 -2
  120. data/lib/shopify-cli/version.rb +1 -1
  121. data/lib/shopify_cli.rb +5 -1
  122. data/shopify.fish +1 -1
  123. data/shopify.sh +1 -1
  124. data/vendor/deps/cli-kit/REVISION +1 -1
  125. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +2 -2
  126. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +3 -3
  127. data/vendor/deps/cli-ui/REVISION +1 -1
  128. data/vendor/deps/cli-ui/lib/cli/ui.rb +26 -22
  129. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +4 -6
  130. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +3 -3
  131. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +8 -9
  132. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +1 -1
  133. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +1 -0
  134. data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +15 -3
  135. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +4 -11
  136. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +3 -5
  137. data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +10 -10
  138. data/vendor/deps/cli-ui/lib/cli/ui/version.rb +1 -1
  139. data/vendor/deps/cli-ui/lib/cli/ui/wrap.rb +56 -0
  140. data/vendor/deps/webrick/.gitignore +9 -0
  141. data/vendor/deps/webrick/Gemfile +3 -0
  142. data/vendor/deps/webrick/LICENSE.txt +22 -0
  143. data/vendor/deps/webrick/README.md +61 -0
  144. data/vendor/deps/webrick/Rakefile +10 -0
  145. data/vendor/deps/webrick/lib/webrick.rb +232 -0
  146. data/vendor/deps/webrick/lib/webrick/accesslog.rb +157 -0
  147. data/vendor/deps/webrick/lib/webrick/cgi.rb +313 -0
  148. data/vendor/deps/webrick/lib/webrick/compat.rb +36 -0
  149. data/vendor/deps/webrick/lib/webrick/config.rb +158 -0
  150. data/vendor/deps/webrick/lib/webrick/cookie.rb +172 -0
  151. data/vendor/deps/webrick/lib/webrick/htmlutils.rb +30 -0
  152. data/vendor/deps/webrick/lib/webrick/httpauth.rb +96 -0
  153. data/vendor/deps/webrick/lib/webrick/httpauth/authenticator.rb +117 -0
  154. data/vendor/deps/webrick/lib/webrick/httpauth/basicauth.rb +116 -0
  155. data/vendor/deps/webrick/lib/webrick/httpauth/digestauth.rb +395 -0
  156. data/vendor/deps/webrick/lib/webrick/httpauth/htdigest.rb +132 -0
  157. data/vendor/deps/webrick/lib/webrick/httpauth/htgroup.rb +97 -0
  158. data/vendor/deps/webrick/lib/webrick/httpauth/htpasswd.rb +158 -0
  159. data/vendor/deps/webrick/lib/webrick/httpauth/userdb.rb +53 -0
  160. data/vendor/deps/webrick/lib/webrick/httpproxy.rb +354 -0
  161. data/vendor/deps/webrick/lib/webrick/httprequest.rb +636 -0
  162. data/vendor/deps/webrick/lib/webrick/httpresponse.rb +564 -0
  163. data/vendor/deps/webrick/lib/webrick/https.rb +152 -0
  164. data/vendor/deps/webrick/lib/webrick/httpserver.rb +294 -0
  165. data/vendor/deps/webrick/lib/webrick/httpservlet.rb +23 -0
  166. data/vendor/deps/webrick/lib/webrick/httpservlet/abstract.rb +152 -0
  167. data/vendor/deps/webrick/lib/webrick/httpservlet/cgi_runner.rb +47 -0
  168. data/vendor/deps/webrick/lib/webrick/httpservlet/cgihandler.rb +126 -0
  169. data/vendor/deps/webrick/lib/webrick/httpservlet/erbhandler.rb +88 -0
  170. data/vendor/deps/webrick/lib/webrick/httpservlet/filehandler.rb +552 -0
  171. data/vendor/deps/webrick/lib/webrick/httpservlet/prochandler.rb +47 -0
  172. data/vendor/deps/webrick/lib/webrick/httpstatus.rb +194 -0
  173. data/vendor/deps/webrick/lib/webrick/httputils.rb +512 -0
  174. data/vendor/deps/webrick/lib/webrick/httpversion.rb +76 -0
  175. data/vendor/deps/webrick/lib/webrick/log.rb +156 -0
  176. data/vendor/deps/webrick/lib/webrick/server.rb +381 -0
  177. data/vendor/deps/webrick/lib/webrick/ssl.rb +215 -0
  178. data/vendor/deps/webrick/lib/webrick/utils.rb +265 -0
  179. data/vendor/deps/webrick/lib/webrick/version.rb +18 -0
  180. data/vendor/deps/webrick/webrick.gemspec +74 -0
  181. metadata +77 -27
  182. data/docs/Gemfile +0 -5
  183. data/docs/Gemfile.lock +0 -258
  184. data/docs/_data/nav.yml +0 -35
  185. data/docs/_includes/footer.html +0 -15
  186. data/docs/_includes/head.html +0 -19
  187. data/docs/_includes/sidebar_nav.html +0 -22
  188. data/docs/_includes/toc.html +0 -112
  189. data/docs/_layouts/default.html +0 -79
  190. data/docs/css/docs.css +0 -157
  191. data/docs/images/header.png +0 -0
  192. data/docs/installing-ruby.md +0 -28
  193. data/lib/project_types/extension/features/argo/admin.rb +0 -20
  194. data/lib/project_types/extension/features/argo/base.rb +0 -129
  195. data/lib/project_types/extension/features/argo/checkout.rb +0 -20
  196. data/lib/project_types/extension/models/type.rb +0 -81
  197. data/lib/project_types/extension/models/types/checkout_post_purchase.rb +0 -23
  198. data/lib/project_types/extension/models/types/product_subscription.rb +0 -24
  199. data/lib/project_types/node/commands/generate/billing.rb +0 -39
  200. data/lib/project_types/node/commands/generate/page.rb +0 -59
  201. data/lib/project_types/node/commands/generate/webhook.rb +0 -37
  202. data/lib/project_types/script/layers/domain/script.rb +0 -18
  203. data/lib/project_types/script/layers/infrastructure/assemblyscript_tsconfig.rb +0 -38
  204. data/lib/project_types/script/layers/infrastructure/script_repository.rb +0 -59
  205. data/lib/project_types/script/templates/ts/as-pect.config.js +0 -27
  206. data/lib/project_types/script/templates/ts/as-pect.d.ts +0 -1
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # httpauth/htdigest.rb -- Apache compatible htdigest file
4
+ #
5
+ # Author: IPR -- Internet Programming with Ruby -- writers
6
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
7
+ # reserved.
8
+ #
9
+ # $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
10
+
11
+ require_relative 'userdb'
12
+ require_relative 'digestauth'
13
+ require 'tempfile'
14
+
15
+ module WEBrick
16
+ module HTTPAuth
17
+
18
+ ##
19
+ # Htdigest accesses apache-compatible digest password files. Passwords are
20
+ # matched to a realm where they are valid. For security, the path for a
21
+ # digest password database should be stored outside of the paths available
22
+ # to the HTTP server.
23
+ #
24
+ # Htdigest is intended for use with WEBrick::HTTPAuth::DigestAuth and
25
+ # stores passwords using cryptographic hashes.
26
+ #
27
+ # htpasswd = WEBrick::HTTPAuth::Htdigest.new 'my_password_file'
28
+ # htpasswd.set_passwd 'my realm', 'username', 'password'
29
+ # htpasswd.flush
30
+
31
+ class Htdigest
32
+ include UserDB
33
+
34
+ ##
35
+ # Open a digest password database at +path+
36
+
37
+ def initialize(path)
38
+ @path = path
39
+ @mtime = Time.at(0)
40
+ @digest = Hash.new
41
+ @mutex = Thread::Mutex::new
42
+ @auth_type = DigestAuth
43
+ File.open(@path,"a").close unless File.exist?(@path)
44
+ reload
45
+ end
46
+
47
+ ##
48
+ # Reloads passwords from the database
49
+
50
+ def reload
51
+ mtime = File::mtime(@path)
52
+ if mtime > @mtime
53
+ @digest.clear
54
+ File.open(@path){|io|
55
+ while line = io.gets
56
+ line.chomp!
57
+ user, realm, pass = line.split(/:/, 3)
58
+ unless @digest[realm]
59
+ @digest[realm] = Hash.new
60
+ end
61
+ @digest[realm][user] = pass
62
+ end
63
+ }
64
+ @mtime = mtime
65
+ end
66
+ end
67
+
68
+ ##
69
+ # Flush the password database. If +output+ is given the database will
70
+ # be written there instead of to the original path.
71
+
72
+ def flush(output=nil)
73
+ output ||= @path
74
+ tmp = Tempfile.create("htpasswd", File::dirname(output))
75
+ renamed = false
76
+ begin
77
+ each{|item| tmp.puts(item.join(":")) }
78
+ tmp.close
79
+ File::rename(tmp.path, output)
80
+ renamed = true
81
+ ensure
82
+ tmp.close
83
+ File.unlink(tmp.path) if !renamed
84
+ end
85
+ end
86
+
87
+ ##
88
+ # Retrieves a password from the database for +user+ in +realm+. If
89
+ # +reload_db+ is true the database will be reloaded first.
90
+
91
+ def get_passwd(realm, user, reload_db)
92
+ reload() if reload_db
93
+ if hash = @digest[realm]
94
+ hash[user]
95
+ end
96
+ end
97
+
98
+ ##
99
+ # Sets a password in the database for +user+ in +realm+ to +pass+.
100
+
101
+ def set_passwd(realm, user, pass)
102
+ @mutex.synchronize{
103
+ unless @digest[realm]
104
+ @digest[realm] = Hash.new
105
+ end
106
+ @digest[realm][user] = make_passwd(realm, user, pass)
107
+ }
108
+ end
109
+
110
+ ##
111
+ # Removes a password from the database for +user+ in +realm+.
112
+
113
+ def delete_passwd(realm, user)
114
+ if hash = @digest[realm]
115
+ hash.delete(user)
116
+ end
117
+ end
118
+
119
+ ##
120
+ # Iterate passwords in the database.
121
+
122
+ def each # :yields: [user, realm, password_hash]
123
+ @digest.keys.sort.each{|realm|
124
+ hash = @digest[realm]
125
+ hash.keys.sort.each{|user|
126
+ yield([user, realm, hash[user]])
127
+ }
128
+ }
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # httpauth/htgroup.rb -- Apache compatible htgroup file
4
+ #
5
+ # Author: IPR -- Internet Programming with Ruby -- writers
6
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
7
+ # reserved.
8
+ #
9
+ # $IPR: htgroup.rb,v 1.1 2003/02/16 22:22:56 gotoyuzo Exp $
10
+
11
+ require 'tempfile'
12
+
13
+ module WEBrick
14
+ module HTTPAuth
15
+
16
+ ##
17
+ # Htgroup accesses apache-compatible group files. Htgroup can be used to
18
+ # provide group-based authentication for users. Currently Htgroup is not
19
+ # directly integrated with any authenticators in WEBrick. For security,
20
+ # the path for a digest password database should be stored outside of the
21
+ # paths available to the HTTP server.
22
+ #
23
+ # Example:
24
+ #
25
+ # htgroup = WEBrick::HTTPAuth::Htgroup.new 'my_group_file'
26
+ # htgroup.add 'superheroes', %w[spiderman batman]
27
+ #
28
+ # htgroup.members('superheroes').include? 'magneto' # => false
29
+
30
+ class Htgroup
31
+
32
+ ##
33
+ # Open a group database at +path+
34
+
35
+ def initialize(path)
36
+ @path = path
37
+ @mtime = Time.at(0)
38
+ @group = Hash.new
39
+ File.open(@path,"a").close unless File.exist?(@path)
40
+ reload
41
+ end
42
+
43
+ ##
44
+ # Reload groups from the database
45
+
46
+ def reload
47
+ if (mtime = File::mtime(@path)) > @mtime
48
+ @group.clear
49
+ File.open(@path){|io|
50
+ while line = io.gets
51
+ line.chomp!
52
+ group, members = line.split(/:\s*/)
53
+ @group[group] = members.split(/\s+/)
54
+ end
55
+ }
56
+ @mtime = mtime
57
+ end
58
+ end
59
+
60
+ ##
61
+ # Flush the group database. If +output+ is given the database will be
62
+ # written there instead of to the original path.
63
+
64
+ def flush(output=nil)
65
+ output ||= @path
66
+ tmp = Tempfile.create("htgroup", File::dirname(output))
67
+ begin
68
+ @group.keys.sort.each{|group|
69
+ tmp.puts(format("%s: %s", group, self.members(group).join(" ")))
70
+ }
71
+ ensure
72
+ tmp.close
73
+ if $!
74
+ File.unlink(tmp.path)
75
+ else
76
+ return File.rename(tmp.path, output)
77
+ end
78
+ end
79
+ end
80
+
81
+ ##
82
+ # Retrieve the list of members from +group+
83
+
84
+ def members(group)
85
+ reload
86
+ @group[group] || []
87
+ end
88
+
89
+ ##
90
+ # Add an Array of +members+ to +group+
91
+
92
+ def add(group, members)
93
+ @group[group] = members(group) | members
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # httpauth/htpasswd -- Apache compatible htpasswd file
4
+ #
5
+ # Author: IPR -- Internet Programming with Ruby -- writers
6
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
7
+ # reserved.
8
+ #
9
+ # $IPR: htpasswd.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
10
+
11
+ require_relative 'userdb'
12
+ require_relative 'basicauth'
13
+ require 'tempfile'
14
+
15
+ module WEBrick
16
+ module HTTPAuth
17
+
18
+ ##
19
+ # Htpasswd accesses apache-compatible password files. Passwords are
20
+ # matched to a realm where they are valid. For security, the path for a
21
+ # password database should be stored outside of the paths available to the
22
+ # HTTP server.
23
+ #
24
+ # Htpasswd is intended for use with WEBrick::HTTPAuth::BasicAuth.
25
+ #
26
+ # To create an Htpasswd database with a single user:
27
+ #
28
+ # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
29
+ # htpasswd.set_passwd 'my realm', 'username', 'password'
30
+ # htpasswd.flush
31
+
32
+ class Htpasswd
33
+ include UserDB
34
+
35
+ ##
36
+ # Open a password database at +path+
37
+
38
+ def initialize(path, password_hash: nil)
39
+ @path = path
40
+ @mtime = Time.at(0)
41
+ @passwd = Hash.new
42
+ @auth_type = BasicAuth
43
+ @password_hash = password_hash
44
+
45
+ case @password_hash
46
+ when nil
47
+ # begin
48
+ # require "string/crypt"
49
+ # rescue LoadError
50
+ # warn("Unable to load string/crypt, proceeding with deprecated use of String#crypt, consider using password_hash: :bcrypt")
51
+ # end
52
+ @password_hash = :crypt
53
+ when :crypt
54
+ # require "string/crypt"
55
+ when :bcrypt
56
+ require "bcrypt"
57
+ else
58
+ raise ArgumentError, "only :crypt and :bcrypt are supported for password_hash keyword argument"
59
+ end
60
+
61
+ File.open(@path,"a").close unless File.exist?(@path)
62
+ reload
63
+ end
64
+
65
+ ##
66
+ # Reload passwords from the database
67
+
68
+ def reload
69
+ mtime = File::mtime(@path)
70
+ if mtime > @mtime
71
+ @passwd.clear
72
+ File.open(@path){|io|
73
+ while line = io.gets
74
+ line.chomp!
75
+ case line
76
+ when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z!
77
+ if @password_hash == :bcrypt
78
+ raise StandardError, ".htpasswd file contains crypt password, only bcrypt passwords supported"
79
+ end
80
+ user, pass = line.split(":")
81
+ when %r!\A[^:]+:\$2[aby]\$\d{2}\$.{53}\z!
82
+ if @password_hash == :crypt
83
+ raise StandardError, ".htpasswd file contains bcrypt password, only crypt passwords supported"
84
+ end
85
+ user, pass = line.split(":")
86
+ when /:\$/, /:{SHA}/
87
+ raise NotImplementedError,
88
+ 'MD5, SHA1 .htpasswd file not supported'
89
+ else
90
+ raise StandardError, 'bad .htpasswd file'
91
+ end
92
+ @passwd[user] = pass
93
+ end
94
+ }
95
+ @mtime = mtime
96
+ end
97
+ end
98
+
99
+ ##
100
+ # Flush the password database. If +output+ is given the database will
101
+ # be written there instead of to the original path.
102
+
103
+ def flush(output=nil)
104
+ output ||= @path
105
+ tmp = Tempfile.create("htpasswd", File::dirname(output))
106
+ renamed = false
107
+ begin
108
+ each{|item| tmp.puts(item.join(":")) }
109
+ tmp.close
110
+ File::rename(tmp.path, output)
111
+ renamed = true
112
+ ensure
113
+ tmp.close
114
+ File.unlink(tmp.path) if !renamed
115
+ end
116
+ end
117
+
118
+ ##
119
+ # Retrieves a password from the database for +user+ in +realm+. If
120
+ # +reload_db+ is true the database will be reloaded first.
121
+
122
+ def get_passwd(realm, user, reload_db)
123
+ reload() if reload_db
124
+ @passwd[user]
125
+ end
126
+
127
+ ##
128
+ # Sets a password in the database for +user+ in +realm+ to +pass+.
129
+
130
+ def set_passwd(realm, user, pass)
131
+ if @password_hash == :bcrypt
132
+ # Cost of 5 to match Apache default, and because the
133
+ # bcrypt default of 10 will introduce significant delays
134
+ # for every request.
135
+ @passwd[user] = BCrypt::Password.create(pass, :cost=>5)
136
+ else
137
+ @passwd[user] = make_passwd(realm, user, pass)
138
+ end
139
+ end
140
+
141
+ ##
142
+ # Removes a password from the database for +user+ in +realm+.
143
+
144
+ def delete_passwd(realm, user)
145
+ @passwd.delete(user)
146
+ end
147
+
148
+ ##
149
+ # Iterate passwords in the database.
150
+
151
+ def each # :yields: [user, password]
152
+ @passwd.keys.sort.each{|user|
153
+ yield([user, @passwd[user]])
154
+ }
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ #--
3
+ # httpauth/userdb.rb -- UserDB mix-in module.
4
+ #
5
+ # Author: IPR -- Internet Programming with Ruby -- writers
6
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
7
+ # reserved.
8
+ #
9
+ # $IPR: userdb.rb,v 1.2 2003/02/20 07:15:48 gotoyuzo Exp $
10
+
11
+ module WEBrick
12
+ module HTTPAuth
13
+
14
+ ##
15
+ # User database mixin for HTTPAuth. This mixin dispatches user record
16
+ # access to the underlying auth_type for this database.
17
+
18
+ module UserDB
19
+
20
+ ##
21
+ # The authentication type.
22
+ #
23
+ # WEBrick::HTTPAuth::BasicAuth or WEBrick::HTTPAuth::DigestAuth are
24
+ # built-in.
25
+
26
+ attr_accessor :auth_type
27
+
28
+ ##
29
+ # Creates an obscured password in +realm+ with +user+ and +password+
30
+ # using the auth_type of this database.
31
+
32
+ def make_passwd(realm, user, pass)
33
+ @auth_type::make_passwd(realm, user, pass)
34
+ end
35
+
36
+ ##
37
+ # Sets a password in +realm+ with +user+ and +password+ for the
38
+ # auth_type of this database.
39
+
40
+ def set_passwd(realm, user, pass)
41
+ self[user] = pass
42
+ end
43
+
44
+ ##
45
+ # Retrieves a password in +realm+ for +user+ for the auth_type of this
46
+ # database. +reload_db+ is a dummy value.
47
+
48
+ def get_passwd(realm, user, reload_db=false)
49
+ make_passwd(realm, user, self[user])
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,354 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # httpproxy.rb -- HTTPProxy Class
4
+ #
5
+ # Author: IPR -- Internet Programming with Ruby -- writers
6
+ # Copyright (c) 2002 GOTO Kentaro
7
+ # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
8
+ # reserved.
9
+ #
10
+ # $IPR: httpproxy.rb,v 1.18 2003/03/08 18:58:10 gotoyuzo Exp $
11
+ # $kNotwork: straw.rb,v 1.3 2002/02/12 15:13:07 gotoken Exp $
12
+
13
+ require_relative "httpserver"
14
+ require "net/http"
15
+
16
+ module WEBrick
17
+
18
+ NullReader = Object.new # :nodoc:
19
+ class << NullReader # :nodoc:
20
+ def read(*args)
21
+ nil
22
+ end
23
+ alias gets read
24
+ end
25
+
26
+ FakeProxyURI = Object.new # :nodoc:
27
+ class << FakeProxyURI # :nodoc:
28
+ def method_missing(meth, *args)
29
+ if %w(scheme host port path query userinfo).member?(meth.to_s)
30
+ return nil
31
+ end
32
+ super
33
+ end
34
+ end
35
+
36
+ # :startdoc:
37
+
38
+ ##
39
+ # An HTTP Proxy server which proxies GET, HEAD and POST requests.
40
+ #
41
+ # To create a simple proxy server:
42
+ #
43
+ # require 'webrick'
44
+ # require 'webrick/httpproxy'
45
+ #
46
+ # proxy = WEBrick::HTTPProxyServer.new Port: 8000
47
+ #
48
+ # trap 'INT' do proxy.shutdown end
49
+ # trap 'TERM' do proxy.shutdown end
50
+ #
51
+ # proxy.start
52
+ #
53
+ # See ::new for proxy-specific configuration items.
54
+ #
55
+ # == Modifying proxied responses
56
+ #
57
+ # To modify content the proxy server returns use the +:ProxyContentHandler+
58
+ # option:
59
+ #
60
+ # handler = proc do |req, res|
61
+ # if res['content-type'] == 'text/plain' then
62
+ # res.body << "\nThis content was proxied!\n"
63
+ # end
64
+ # end
65
+ #
66
+ # proxy =
67
+ # WEBrick::HTTPProxyServer.new Port: 8000, ProxyContentHandler: handler
68
+
69
+ class HTTPProxyServer < HTTPServer
70
+
71
+ ##
72
+ # Proxy server configurations. The proxy server handles the following
73
+ # configuration items in addition to those supported by HTTPServer:
74
+ #
75
+ # :ProxyAuthProc:: Called with a request and response to authorize a
76
+ # request
77
+ # :ProxyVia:: Appended to the via header
78
+ # :ProxyURI:: The proxy server's URI
79
+ # :ProxyContentHandler:: Called with a request and response and allows
80
+ # modification of the response
81
+ # :ProxyTimeout:: Sets the proxy timeouts to 30 seconds for open and 60
82
+ # seconds for read operations
83
+
84
+ def initialize(config={}, default=Config::HTTP)
85
+ super(config, default)
86
+ c = @config
87
+ @via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}"
88
+ end
89
+
90
+ # :stopdoc:
91
+ def service(req, res)
92
+ if req.request_method == "CONNECT"
93
+ do_CONNECT(req, res)
94
+ elsif req.unparsed_uri =~ %r!^http://!
95
+ proxy_service(req, res)
96
+ else
97
+ super(req, res)
98
+ end
99
+ end
100
+
101
+ def proxy_auth(req, res)
102
+ if proc = @config[:ProxyAuthProc]
103
+ proc.call(req, res)
104
+ end
105
+ req.header.delete("proxy-authorization")
106
+ end
107
+
108
+ def proxy_uri(req, res)
109
+ # should return upstream proxy server's URI
110
+ return @config[:ProxyURI]
111
+ end
112
+
113
+ def proxy_service(req, res)
114
+ # Proxy Authentication
115
+ proxy_auth(req, res)
116
+
117
+ begin
118
+ public_send("do_#{req.request_method}", req, res)
119
+ rescue NoMethodError
120
+ raise HTTPStatus::MethodNotAllowed,
121
+ "unsupported method `#{req.request_method}'."
122
+ rescue => err
123
+ logger.debug("#{err.class}: #{err.message}")
124
+ raise HTTPStatus::ServiceUnavailable, err.message
125
+ end
126
+
127
+ # Process contents
128
+ if handler = @config[:ProxyContentHandler]
129
+ handler.call(req, res)
130
+ end
131
+ end
132
+
133
+ def do_CONNECT(req, res)
134
+ # Proxy Authentication
135
+ proxy_auth(req, res)
136
+
137
+ ua = Thread.current[:WEBrickSocket] # User-Agent
138
+ raise HTTPStatus::InternalServerError,
139
+ "[BUG] cannot get socket" unless ua
140
+
141
+ host, port = req.unparsed_uri.split(":", 2)
142
+ # Proxy authentication for upstream proxy server
143
+ if proxy = proxy_uri(req, res)
144
+ proxy_request_line = "CONNECT #{host}:#{port} HTTP/1.0"
145
+ if proxy.userinfo
146
+ credentials = "Basic " + [proxy.userinfo].pack("m0")
147
+ end
148
+ host, port = proxy.host, proxy.port
149
+ end
150
+
151
+ begin
152
+ @logger.debug("CONNECT: upstream proxy is `#{host}:#{port}'.")
153
+ os = TCPSocket.new(host, port) # origin server
154
+
155
+ if proxy
156
+ @logger.debug("CONNECT: sending a Request-Line")
157
+ os << proxy_request_line << CRLF
158
+ @logger.debug("CONNECT: > #{proxy_request_line}")
159
+ if credentials
160
+ @logger.debug("CONNECT: sending credentials")
161
+ os << "Proxy-Authorization: " << credentials << CRLF
162
+ end
163
+ os << CRLF
164
+ proxy_status_line = os.gets(LF)
165
+ @logger.debug("CONNECT: read Status-Line from the upstream server")
166
+ @logger.debug("CONNECT: < #{proxy_status_line}")
167
+ if %r{^HTTP/\d+\.\d+\s+200\s*} =~ proxy_status_line
168
+ while line = os.gets(LF)
169
+ break if /\A(#{CRLF}|#{LF})\z/om =~ line
170
+ end
171
+ else
172
+ raise HTTPStatus::BadGateway
173
+ end
174
+ end
175
+ @logger.debug("CONNECT #{host}:#{port}: succeeded")
176
+ res.status = HTTPStatus::RC_OK
177
+ rescue => ex
178
+ @logger.debug("CONNECT #{host}:#{port}: failed `#{ex.message}'")
179
+ res.set_error(ex)
180
+ raise HTTPStatus::EOFError
181
+ ensure
182
+ if handler = @config[:ProxyContentHandler]
183
+ handler.call(req, res)
184
+ end
185
+ res.send_response(ua)
186
+ access_log(@config, req, res)
187
+
188
+ # Should clear request-line not to send the response twice.
189
+ # see: HTTPServer#run
190
+ req.parse(NullReader) rescue nil
191
+ end
192
+
193
+ begin
194
+ while fds = IO::select([ua, os])
195
+ if fds[0].member?(ua)
196
+ buf = ua.readpartial(1024);
197
+ @logger.debug("CONNECT: #{buf.bytesize} byte from User-Agent")
198
+ os.write(buf)
199
+ elsif fds[0].member?(os)
200
+ buf = os.readpartial(1024);
201
+ @logger.debug("CONNECT: #{buf.bytesize} byte from #{host}:#{port}")
202
+ ua.write(buf)
203
+ end
204
+ end
205
+ rescue
206
+ os.close
207
+ @logger.debug("CONNECT #{host}:#{port}: closed")
208
+ end
209
+
210
+ raise HTTPStatus::EOFError
211
+ end
212
+
213
+ def do_GET(req, res)
214
+ perform_proxy_request(req, res, Net::HTTP::Get)
215
+ end
216
+
217
+ def do_HEAD(req, res)
218
+ perform_proxy_request(req, res, Net::HTTP::Head)
219
+ end
220
+
221
+ def do_POST(req, res)
222
+ perform_proxy_request(req, res, Net::HTTP::Post, req.body_reader)
223
+ end
224
+
225
+ def do_OPTIONS(req, res)
226
+ res['allow'] = "GET,HEAD,POST,OPTIONS,CONNECT"
227
+ end
228
+
229
+ private
230
+
231
+ # Some header fields should not be transferred.
232
+ HopByHop = %w( connection keep-alive proxy-authenticate upgrade
233
+ proxy-authorization te trailers transfer-encoding )
234
+ ShouldNotTransfer = %w( set-cookie proxy-connection )
235
+ def split_field(f) f ? f.split(/,\s+/).collect{|i| i.downcase } : [] end
236
+
237
+ def choose_header(src, dst)
238
+ connections = split_field(src['connection'])
239
+ src.each{|key, value|
240
+ key = key.downcase
241
+ if HopByHop.member?(key) || # RFC2616: 13.5.1
242
+ connections.member?(key) || # RFC2616: 14.10
243
+ ShouldNotTransfer.member?(key) # pragmatics
244
+ @logger.debug("choose_header: `#{key}: #{value}'")
245
+ next
246
+ end
247
+ dst[key] = value
248
+ }
249
+ end
250
+
251
+ # Net::HTTP is stupid about the multiple header fields.
252
+ # Here is workaround:
253
+ def set_cookie(src, dst)
254
+ if str = src['set-cookie']
255
+ cookies = []
256
+ str.split(/,\s*/).each{|token|
257
+ if /^[^=]+;/o =~ token
258
+ cookies[-1] << ", " << token
259
+ elsif /=/o =~ token
260
+ cookies << token
261
+ else
262
+ cookies[-1] << ", " << token
263
+ end
264
+ }
265
+ dst.cookies.replace(cookies)
266
+ end
267
+ end
268
+
269
+ def set_via(h)
270
+ if @config[:ProxyVia]
271
+ if h['via']
272
+ h['via'] << ", " << @via
273
+ else
274
+ h['via'] = @via
275
+ end
276
+ end
277
+ end
278
+
279
+ def setup_proxy_header(req, res)
280
+ # Choose header fields to transfer
281
+ header = Hash.new
282
+ choose_header(req, header)
283
+ set_via(header)
284
+ return header
285
+ end
286
+
287
+ def setup_upstream_proxy_authentication(req, res, header)
288
+ if upstream = proxy_uri(req, res)
289
+ if upstream.userinfo
290
+ header['proxy-authorization'] =
291
+ "Basic " + [upstream.userinfo].pack("m0")
292
+ end
293
+ return upstream
294
+ end
295
+ return FakeProxyURI
296
+ end
297
+
298
+ def create_net_http(uri, upstream)
299
+ Net::HTTP.new(uri.host, uri.port, upstream.host, upstream.port)
300
+ end
301
+
302
+ def perform_proxy_request(req, res, req_class, body_stream = nil)
303
+ uri = req.request_uri
304
+ path = uri.path.dup
305
+ path << "?" << uri.query if uri.query
306
+ header = setup_proxy_header(req, res)
307
+ upstream = setup_upstream_proxy_authentication(req, res, header)
308
+
309
+ body_tmp = []
310
+ http = create_net_http(uri, upstream)
311
+ req_fib = Fiber.new do
312
+ http.start do
313
+ if @config[:ProxyTimeout]
314
+ ################################## these issues are
315
+ http.open_timeout = 30 # secs # necessary (maybe because
316
+ http.read_timeout = 60 # secs # Ruby's bug, but why?)
317
+ ##################################
318
+ end
319
+ if body_stream && req['transfer-encoding'] =~ /\bchunked\b/i
320
+ header['Transfer-Encoding'] = 'chunked'
321
+ end
322
+ http_req = req_class.new(path, header)
323
+ http_req.body_stream = body_stream if body_stream
324
+ http.request(http_req) do |response|
325
+ # Persistent connection requirements are mysterious for me.
326
+ # So I will close the connection in every response.
327
+ res['proxy-connection'] = "close"
328
+ res['connection'] = "close"
329
+
330
+ # stream Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
331
+ res.status = response.code.to_i
332
+ res.chunked = response.chunked?
333
+ choose_header(response, res)
334
+ set_cookie(response, res)
335
+ set_via(res)
336
+ response.read_body do |buf|
337
+ body_tmp << buf
338
+ Fiber.yield # wait for res.body Proc#call
339
+ end
340
+ end # http.request
341
+ end
342
+ end
343
+ req_fib.resume # read HTTP response headers and first chunk of the body
344
+ res.body = ->(socket) do
345
+ while buf = body_tmp.shift
346
+ socket.write(buf)
347
+ buf.clear
348
+ req_fib.resume # continue response.read_body
349
+ end
350
+ end
351
+ end
352
+ # :stopdoc:
353
+ end
354
+ end