fluid_cli 0.1.2
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.
- checksums.yaml +7 -0
- data/README.md +1 -0
- data/dev.yml +5 -0
- data/exe/fluid +24 -0
- data/lib/fluid_cli/api.rb +135 -0
- data/lib/fluid_cli/assets/post_auth_page/index.html.erb +34 -0
- data/lib/fluid_cli/assets/post_auth_page/style.css +58 -0
- data/lib/fluid_cli/command.rb +55 -0
- data/lib/fluid_cli/commands/help.rb +21 -0
- data/lib/fluid_cli/commands/login.rb +30 -0
- data/lib/fluid_cli/commands/logout.rb +38 -0
- data/lib/fluid_cli/commands/switch.rb +23 -0
- data/lib/fluid_cli/commands/theme/common/company_helper.rb +15 -0
- data/lib/fluid_cli/commands/theme/common/root_helper.rb +95 -0
- data/lib/fluid_cli/commands/theme/dev.rb +61 -0
- data/lib/fluid_cli/commands/theme/help.rb +21 -0
- data/lib/fluid_cli/commands/theme/init.rb +46 -0
- data/lib/fluid_cli/commands/theme/pull.rb +68 -0
- data/lib/fluid_cli/commands/theme/push.rb +132 -0
- data/lib/fluid_cli/commands/theme.rb +23 -0
- data/lib/fluid_cli/commands/whoami.rb +23 -0
- data/lib/fluid_cli/commands.rb +19 -0
- data/lib/fluid_cli/company_switcher.rb +69 -0
- data/lib/fluid_cli/context.rb +691 -0
- data/lib/fluid_cli/db.rb +114 -0
- data/lib/fluid_cli/entry_point.rb +10 -0
- data/lib/fluid_cli/environment.rb +32 -0
- data/lib/fluid_cli/file_system_listener.rb +29 -0
- data/lib/fluid_cli/form.rb +42 -0
- data/lib/fluid_cli/git.rb +319 -0
- data/lib/fluid_cli/http_request.rb +54 -0
- data/lib/fluid_cli/identity_auth/servlet.rb +39 -0
- data/lib/fluid_cli/identity_auth.rb +126 -0
- data/lib/fluid_cli/options.rb +38 -0
- data/lib/fluid_cli/theme/dev_server/certificate_manager.rb +79 -0
- data/lib/fluid_cli/theme/dev_server/errors.rb +9 -0
- data/lib/fluid_cli/theme/dev_server/header_hash.rb +98 -0
- data/lib/fluid_cli/theme/dev_server/hooks/file_change_hook.rb +39 -0
- data/lib/fluid_cli/theme/dev_server/hot_reload/resources/hot-reload-no-script.html +27 -0
- data/lib/fluid_cli/theme/dev_server/hot_reload/resources/hot_reload.js +28 -0
- data/lib/fluid_cli/theme/dev_server/hot_reload/resources/sse_client.js +43 -0
- data/lib/fluid_cli/theme/dev_server/hot_reload/resources/theme.js +16 -0
- data/lib/fluid_cli/theme/dev_server/hot_reload/script_injector.rb +54 -0
- data/lib/fluid_cli/theme/dev_server/hot_reload.rb +75 -0
- data/lib/fluid_cli/theme/dev_server/local_assets.rb +92 -0
- data/lib/fluid_cli/theme/dev_server/proxy.rb +235 -0
- data/lib/fluid_cli/theme/dev_server/proxy_param_builder.rb +82 -0
- data/lib/fluid_cli/theme/dev_server/reload_mode.rb +34 -0
- data/lib/fluid_cli/theme/dev_server/sse.rb +75 -0
- data/lib/fluid_cli/theme/dev_server/watcher.rb +57 -0
- data/lib/fluid_cli/theme/dev_server/web_server.rb +140 -0
- data/lib/fluid_cli/theme/dev_server.rb +289 -0
- data/lib/fluid_cli/theme/development_theme.rb +101 -0
- data/lib/fluid_cli/theme/file.rb +105 -0
- data/lib/fluid_cli/theme/forms/select.rb +33 -0
- data/lib/fluid_cli/theme/mime_type.rb +34 -0
- data/lib/fluid_cli/theme/presenters/theme_presenter.rb +49 -0
- data/lib/fluid_cli/theme/presenters/themes_presenter.rb +31 -0
- data/lib/fluid_cli/theme/root.rb +62 -0
- data/lib/fluid_cli/theme/syncer/checksums.rb +66 -0
- data/lib/fluid_cli/theme/syncer/downloader.rb +54 -0
- data/lib/fluid_cli/theme/syncer/error_reporter.rb +45 -0
- data/lib/fluid_cli/theme/syncer/merger.rb +53 -0
- data/lib/fluid_cli/theme/syncer/operation.rb +58 -0
- data/lib/fluid_cli/theme/syncer/standard_reporter.rb +32 -0
- data/lib/fluid_cli/theme/syncer/unsupported_script_warning.rb +90 -0
- data/lib/fluid_cli/theme/syncer/uploader/forms/apply_to_all.rb +41 -0
- data/lib/fluid_cli/theme/syncer/uploader/forms/apply_to_all_form.rb +37 -0
- data/lib/fluid_cli/theme/syncer/uploader/forms/base_strategy_form.rb +64 -0
- data/lib/fluid_cli/theme/syncer/uploader/forms/select_delete_strategy.rb +29 -0
- data/lib/fluid_cli/theme/syncer/uploader/forms/select_update_strategy.rb +30 -0
- data/lib/fluid_cli/theme/syncer/uploader/json_delete_handler.rb +49 -0
- data/lib/fluid_cli/theme/syncer/uploader/json_update_handler.rb +71 -0
- data/lib/fluid_cli/theme/syncer/uploader.rb +105 -0
- data/lib/fluid_cli/theme/syncer.rb +412 -0
- data/lib/fluid_cli/theme/theme.rb +186 -0
- data/lib/fluid_cli/theme/ui/sync_progress_bar.rb +22 -0
- data/lib/fluid_cli/thread_pool/job.rb +35 -0
- data/lib/fluid_cli/thread_pool.rb +49 -0
- data/lib/fluid_cli/version.rb +3 -0
- data/lib/fluid_cli.rb +59 -0
- data/vendor/deps/base64/.document +5 -0
- data/vendor/deps/base64/.gitignore +9 -0
- data/vendor/deps/base64/BSDL +22 -0
- data/vendor/deps/base64/COPYING +56 -0
- data/vendor/deps/base64/Gemfile +9 -0
- data/vendor/deps/base64/LEGAL +60 -0
- data/vendor/deps/base64/README.md +48 -0
- data/vendor/deps/base64/Rakefile +31 -0
- data/vendor/deps/base64/base64.gemspec +28 -0
- data/vendor/deps/base64/bin/console +14 -0
- data/vendor/deps/base64/bin/setup +8 -0
- data/vendor/deps/base64/lib/base64.rb +382 -0
- data/vendor/deps/base64/sig/base64.rbs +358 -0
- data/vendor/deps/base64/test/base64/test_base64.rb +115 -0
- data/vendor/deps/base64/test_sig/test_base64.rb +44 -0
- data/vendor/deps/cli-kit/REVISION +1 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/definition.rb +286 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/evaluation.rb +215 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/parser/node.rb +128 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/parser.rb +125 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/tokenizer.rb +130 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args.rb +16 -0
- data/vendor/deps/cli-kit/lib/cli/kit/base_command.rb +30 -0
- data/vendor/deps/cli-kit/lib/cli/kit/command_help.rb +268 -0
- data/vendor/deps/cli-kit/lib/cli/kit/command_registry.rb +150 -0
- data/vendor/deps/cli-kit/lib/cli/kit/config.rb +137 -0
- data/vendor/deps/cli-kit/lib/cli/kit/core_ext.rb +28 -0
- data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +166 -0
- data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +92 -0
- data/vendor/deps/cli-kit/lib/cli/kit/ini.rb +91 -0
- data/vendor/deps/cli-kit/lib/cli/kit/levenshtein.rb +92 -0
- data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +94 -0
- data/vendor/deps/cli-kit/lib/cli/kit/opts.rb +248 -0
- data/vendor/deps/cli-kit/lib/cli/kit/parse_args.rb +55 -0
- data/vendor/deps/cli-kit/lib/cli/kit/resolver.rb +66 -0
- data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +260 -0
- data/vendor/deps/cli-kit/lib/cli/kit/support.rb +11 -0
- data/vendor/deps/cli-kit/lib/cli/kit/system.rb +290 -0
- data/vendor/deps/cli-kit/lib/cli/kit/util.rb +118 -0
- data/vendor/deps/cli-kit/lib/cli/kit/version.rb +7 -0
- data/vendor/deps/cli-kit/lib/cli/kit.rb +139 -0
- data/vendor/deps/cli-ui/REVISION +1 -0
- data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +218 -0
- data/vendor/deps/cli-ui/lib/cli/ui/color.rb +101 -0
- data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +219 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +67 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/box.rb +179 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/bracket.rb +152 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +127 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +286 -0
- data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +92 -0
- data/vendor/deps/cli-ui/lib/cli/ui/os.rb +63 -0
- data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +64 -0
- data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +132 -0
- data/vendor/deps/cli-ui/lib/cli/ui/progress_reporter.rb +209 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +583 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +36 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +381 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/async.rb +48 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +602 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +79 -0
- data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +399 -0
- data/vendor/deps/cli-ui/lib/cli/ui/table.rb +83 -0
- data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +55 -0
- data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +106 -0
- data/vendor/deps/cli-ui/lib/cli/ui/version.rb +8 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets/base.rb +46 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets/status.rb +79 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets.rb +89 -0
- data/vendor/deps/cli-ui/lib/cli/ui/work_queue.rb +142 -0
- data/vendor/deps/cli-ui/lib/cli/ui/wrap.rb +61 -0
- data/vendor/deps/cli-ui/lib/cli/ui.rb +359 -0
- data/vendor/deps/cli-ui/vendor/reentrant_mutex.rb +78 -0
- data/vendor/deps/debug/CONTRIBUTING.md +573 -0
- data/vendor/deps/debug/Gemfile +10 -0
- data/vendor/deps/debug/LICENSE.txt +22 -0
- data/vendor/deps/debug/README.md +996 -0
- data/vendor/deps/debug/Rakefile +57 -0
- data/vendor/deps/debug/TODO.md +23 -0
- data/vendor/deps/debug/debug.gemspec +33 -0
- data/vendor/deps/debug/exe/rdbg +53 -0
- data/vendor/deps/debug/ext/debug/Makefile +273 -0
- data/vendor/deps/debug/ext/debug/debug.c +228 -0
- data/vendor/deps/debug/ext/debug/debug_version.h +1 -0
- data/vendor/deps/debug/ext/debug/extconf.rb +27 -0
- data/vendor/deps/debug/ext/debug/iseq_collector.c +93 -0
- data/vendor/deps/debug/lib/debug/abbrev_command.rb +77 -0
- data/vendor/deps/debug/lib/debug/breakpoint.rb +556 -0
- data/vendor/deps/debug/lib/debug/client.rb +263 -0
- data/vendor/deps/debug/lib/debug/color.rb +123 -0
- data/vendor/deps/debug/lib/debug/config.rb +592 -0
- data/vendor/deps/debug/lib/debug/console.rb +224 -0
- data/vendor/deps/debug/lib/debug/dap_custom/traceInspector.rb +336 -0
- data/vendor/deps/debug/lib/debug/debug.bundle +0 -0
- data/vendor/deps/debug/lib/debug/frame_info.rb +190 -0
- data/vendor/deps/debug/lib/debug/irb_integration.rb +37 -0
- data/vendor/deps/debug/lib/debug/local.rb +115 -0
- data/vendor/deps/debug/lib/debug/open.rb +13 -0
- data/vendor/deps/debug/lib/debug/open_nonstop.rb +15 -0
- data/vendor/deps/debug/lib/debug/prelude.rb +50 -0
- data/vendor/deps/debug/lib/debug/server.rb +534 -0
- data/vendor/deps/debug/lib/debug/server_cdp.rb +1348 -0
- data/vendor/deps/debug/lib/debug/server_dap.rb +1108 -0
- data/vendor/deps/debug/lib/debug/session.rb +2667 -0
- data/vendor/deps/debug/lib/debug/source_repository.rb +150 -0
- data/vendor/deps/debug/lib/debug/start.rb +5 -0
- data/vendor/deps/debug/lib/debug/thread_client.rb +1457 -0
- data/vendor/deps/debug/lib/debug/tracer.rb +241 -0
- data/vendor/deps/debug/lib/debug/version.rb +5 -0
- data/vendor/deps/debug/lib/debug.rb +9 -0
- data/vendor/deps/debug/misc/README.md.erb +660 -0
- data/vendor/deps/listen/.github/release-drafter.yml +17 -0
- data/vendor/deps/listen/.github/workflows/development.yml +67 -0
- data/vendor/deps/listen/.github/workflows/push.yml +12 -0
- data/vendor/deps/listen/.gitignore +28 -0
- data/vendor/deps/listen/.rspec +3 -0
- data/vendor/deps/listen/.rubocop.yml +283 -0
- data/vendor/deps/listen/.yardopts +11 -0
- data/vendor/deps/listen/CHANGELOG.md +1 -0
- data/vendor/deps/listen/CONTRIBUTING.md +45 -0
- data/vendor/deps/listen/Gemfile +33 -0
- data/vendor/deps/listen/Guardfile +26 -0
- data/vendor/deps/listen/LICENSE.txt +22 -0
- data/vendor/deps/listen/README.md +490 -0
- data/vendor/deps/listen/Rakefile +154 -0
- data/vendor/deps/listen/bin/listen +11 -0
- data/vendor/deps/listen/lib/listen/adapter/base.rb +129 -0
- data/vendor/deps/listen/lib/listen/adapter/bsd.rb +104 -0
- data/vendor/deps/listen/lib/listen/adapter/config.rb +31 -0
- data/vendor/deps/listen/lib/listen/adapter/darwin.rb +77 -0
- data/vendor/deps/listen/lib/listen/adapter/linux.rb +108 -0
- data/vendor/deps/listen/lib/listen/adapter/polling.rb +40 -0
- data/vendor/deps/listen/lib/listen/adapter/windows.rb +96 -0
- data/vendor/deps/listen/lib/listen/adapter.rb +43 -0
- data/vendor/deps/listen/lib/listen/backend.rb +40 -0
- data/vendor/deps/listen/lib/listen/change.rb +69 -0
- data/vendor/deps/listen/lib/listen/cli.rb +65 -0
- data/vendor/deps/listen/lib/listen/directory.rb +93 -0
- data/vendor/deps/listen/lib/listen/error.rb +11 -0
- data/vendor/deps/listen/lib/listen/event/config.rb +39 -0
- data/vendor/deps/listen/lib/listen/event/loop.rb +92 -0
- data/vendor/deps/listen/lib/listen/event/processor.rb +128 -0
- data/vendor/deps/listen/lib/listen/event/queue.rb +52 -0
- data/vendor/deps/listen/lib/listen/file.rb +95 -0
- data/vendor/deps/listen/lib/listen/fsm.rb +131 -0
- data/vendor/deps/listen/lib/listen/listener/config.rb +41 -0
- data/vendor/deps/listen/lib/listen/listener.rb +136 -0
- data/vendor/deps/listen/lib/listen/logger.rb +65 -0
- data/vendor/deps/listen/lib/listen/monotonic_time.rb +27 -0
- data/vendor/deps/listen/lib/listen/options.rb +24 -0
- data/vendor/deps/listen/lib/listen/queue_optimizer.rb +129 -0
- data/vendor/deps/listen/lib/listen/record/entry.rb +66 -0
- data/vendor/deps/listen/lib/listen/record/symlink_detector.rb +47 -0
- data/vendor/deps/listen/lib/listen/record.rb +122 -0
- data/vendor/deps/listen/lib/listen/silencer/controller.rb +50 -0
- data/vendor/deps/listen/lib/listen/silencer.rb +106 -0
- data/vendor/deps/listen/lib/listen/thread.rb +54 -0
- data/vendor/deps/listen/lib/listen/version.rb +5 -0
- data/vendor/deps/listen/lib/listen.rb +47 -0
- data/vendor/deps/listen/listen.gemspec +40 -0
- data/vendor/deps/listen/spec/acceptance/listen_spec.rb +320 -0
- data/vendor/deps/listen/spec/lib/listen/adapter/base_spec.rb +101 -0
- data/vendor/deps/listen/spec/lib/listen/adapter/bsd_spec.rb +13 -0
- data/vendor/deps/listen/spec/lib/listen/adapter/config_spec.rb +122 -0
- data/vendor/deps/listen/spec/lib/listen/adapter/darwin_spec.rb +82 -0
- data/vendor/deps/listen/spec/lib/listen/adapter/linux_spec.rb +199 -0
- data/vendor/deps/listen/spec/lib/listen/adapter/polling_spec.rb +83 -0
- data/vendor/deps/listen/spec/lib/listen/adapter/windows_spec.rb +13 -0
- data/vendor/deps/listen/spec/lib/listen/adapter_spec.rb +69 -0
- data/vendor/deps/listen/spec/lib/listen/backend_spec.rb +82 -0
- data/vendor/deps/listen/spec/lib/listen/change_spec.rb +102 -0
- data/vendor/deps/listen/spec/lib/listen/cli_spec.rb +116 -0
- data/vendor/deps/listen/spec/lib/listen/directory_spec.rb +284 -0
- data/vendor/deps/listen/spec/lib/listen/event/config_spec.rb +33 -0
- data/vendor/deps/listen/spec/lib/listen/event/loop_spec.rb +118 -0
- data/vendor/deps/listen/spec/lib/listen/event/processor_spec.rb +250 -0
- data/vendor/deps/listen/spec/lib/listen/event/queue_spec.rb +118 -0
- data/vendor/deps/listen/spec/lib/listen/file_spec.rb +254 -0
- data/vendor/deps/listen/spec/lib/listen/fsm_spec.rb +147 -0
- data/vendor/deps/listen/spec/lib/listen/listener/config_spec.rb +29 -0
- data/vendor/deps/listen/spec/lib/listen/listener_spec.rb +321 -0
- data/vendor/deps/listen/spec/lib/listen/logger_spec.rb +212 -0
- data/vendor/deps/listen/spec/lib/listen/monotonic_time_spec.rb +58 -0
- data/vendor/deps/listen/spec/lib/listen/queue_optimizer_spec.rb +111 -0
- data/vendor/deps/listen/spec/lib/listen/record_spec.rb +424 -0
- data/vendor/deps/listen/spec/lib/listen/silencer/controller_spec.rb +97 -0
- data/vendor/deps/listen/spec/lib/listen/silencer_spec.rb +109 -0
- data/vendor/deps/listen/spec/lib/listen/thread_spec.rb +133 -0
- data/vendor/deps/listen/spec/lib/listen_spec.rb +25 -0
- data/vendor/deps/listen/spec/spec_helper.rb +49 -0
- data/vendor/deps/listen/spec/support/acceptance_helper.rb +260 -0
- data/vendor/deps/listen/spec/support/fixtures_helper.rb +32 -0
- data/vendor/deps/listen/spec/support/platform_helper.rb +17 -0
- data/vendor/deps/observer/.github/dependabot.yml +6 -0
- data/vendor/deps/observer/.github/workflows/test.yml +33 -0
- data/vendor/deps/observer/.gitignore +8 -0
- data/vendor/deps/observer/BSDL +22 -0
- data/vendor/deps/observer/COPYING +56 -0
- data/vendor/deps/observer/Gemfile +9 -0
- data/vendor/deps/observer/README.md +139 -0
- data/vendor/deps/observer/Rakefile +10 -0
- data/vendor/deps/observer/bin/console +14 -0
- data/vendor/deps/observer/bin/setup +8 -0
- data/vendor/deps/observer/lib/observer.rb +229 -0
- data/vendor/deps/observer/observer.gemspec +32 -0
- data/vendor/deps/observer/test/test_observer.rb +66 -0
- data/vendor/deps/webrick/.gitignore +9 -0
- data/vendor/deps/webrick/Gemfile +3 -0
- data/vendor/deps/webrick/LICENSE.txt +22 -0
- data/vendor/deps/webrick/README.md +61 -0
- data/vendor/deps/webrick/Rakefile +10 -0
- data/vendor/deps/webrick/lib/webrick/accesslog.rb +157 -0
- data/vendor/deps/webrick/lib/webrick/cgi.rb +313 -0
- data/vendor/deps/webrick/lib/webrick/compat.rb +36 -0
- data/vendor/deps/webrick/lib/webrick/config.rb +158 -0
- data/vendor/deps/webrick/lib/webrick/cookie.rb +172 -0
- data/vendor/deps/webrick/lib/webrick/htmlutils.rb +30 -0
- data/vendor/deps/webrick/lib/webrick/httpauth/authenticator.rb +117 -0
- data/vendor/deps/webrick/lib/webrick/httpauth/basicauth.rb +116 -0
- data/vendor/deps/webrick/lib/webrick/httpauth/digestauth.rb +395 -0
- data/vendor/deps/webrick/lib/webrick/httpauth/htdigest.rb +132 -0
- data/vendor/deps/webrick/lib/webrick/httpauth/htgroup.rb +97 -0
- data/vendor/deps/webrick/lib/webrick/httpauth/htpasswd.rb +158 -0
- data/vendor/deps/webrick/lib/webrick/httpauth/userdb.rb +53 -0
- data/vendor/deps/webrick/lib/webrick/httpauth.rb +96 -0
- data/vendor/deps/webrick/lib/webrick/httpproxy.rb +354 -0
- data/vendor/deps/webrick/lib/webrick/httprequest.rb +636 -0
- data/vendor/deps/webrick/lib/webrick/httpresponse.rb +564 -0
- data/vendor/deps/webrick/lib/webrick/https.rb +152 -0
- data/vendor/deps/webrick/lib/webrick/httpserver.rb +294 -0
- data/vendor/deps/webrick/lib/webrick/httpservlet/abstract.rb +152 -0
- data/vendor/deps/webrick/lib/webrick/httpservlet/cgi_runner.rb +47 -0
- data/vendor/deps/webrick/lib/webrick/httpservlet/cgihandler.rb +126 -0
- data/vendor/deps/webrick/lib/webrick/httpservlet/erbhandler.rb +88 -0
- data/vendor/deps/webrick/lib/webrick/httpservlet/filehandler.rb +552 -0
- data/vendor/deps/webrick/lib/webrick/httpservlet/prochandler.rb +47 -0
- data/vendor/deps/webrick/lib/webrick/httpservlet.rb +23 -0
- data/vendor/deps/webrick/lib/webrick/httpstatus.rb +194 -0
- data/vendor/deps/webrick/lib/webrick/httputils.rb +512 -0
- data/vendor/deps/webrick/lib/webrick/httpversion.rb +76 -0
- data/vendor/deps/webrick/lib/webrick/log.rb +156 -0
- data/vendor/deps/webrick/lib/webrick/server.rb +381 -0
- data/vendor/deps/webrick/lib/webrick/ssl.rb +215 -0
- data/vendor/deps/webrick/lib/webrick/utils.rb +265 -0
- data/vendor/deps/webrick/lib/webrick/version.rb +18 -0
- data/vendor/deps/webrick/lib/webrick.rb +232 -0
- data/vendor/deps/webrick/webrick.gemspec +74 -0
- metadata +412 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
#
|
|
3
|
+
# httpauth/digestauth.rb -- HTTP digest access authentication
|
|
4
|
+
#
|
|
5
|
+
# Author: IPR -- Internet Programming with Ruby -- writers
|
|
6
|
+
# Copyright (c) 2003 Internet Programming with Ruby writers.
|
|
7
|
+
# Copyright (c) 2003 H.M.
|
|
8
|
+
#
|
|
9
|
+
# The original implementation is provided by H.M.
|
|
10
|
+
# URL: http://rwiki.jin.gr.jp/cgi-bin/rw-cgi.rb?cmd=view;name=
|
|
11
|
+
# %C7%A7%BE%DA%B5%A1%C7%BD%A4%F2%B2%FE%C2%A4%A4%B7%A4%C6%A4%DF%A4%EB
|
|
12
|
+
#
|
|
13
|
+
# $IPR: digestauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
|
|
14
|
+
|
|
15
|
+
require_relative '../config'
|
|
16
|
+
require_relative '../httpstatus'
|
|
17
|
+
require_relative 'authenticator'
|
|
18
|
+
require 'digest/md5'
|
|
19
|
+
require 'digest/sha1'
|
|
20
|
+
|
|
21
|
+
module WEBrick
|
|
22
|
+
module HTTPAuth
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# RFC 2617 Digest Access Authentication for WEBrick
|
|
26
|
+
#
|
|
27
|
+
# Use this class to add digest authentication to a WEBrick servlet.
|
|
28
|
+
#
|
|
29
|
+
# Here is an example of how to set up DigestAuth:
|
|
30
|
+
#
|
|
31
|
+
# config = { :Realm => 'DigestAuth example realm' }
|
|
32
|
+
#
|
|
33
|
+
# htdigest = WEBrick::HTTPAuth::Htdigest.new 'my_password_file'
|
|
34
|
+
# htdigest.set_passwd config[:Realm], 'username', 'password'
|
|
35
|
+
# htdigest.flush
|
|
36
|
+
#
|
|
37
|
+
# config[:UserDB] = htdigest
|
|
38
|
+
#
|
|
39
|
+
# digest_auth = WEBrick::HTTPAuth::DigestAuth.new config
|
|
40
|
+
#
|
|
41
|
+
# When using this as with a servlet be sure not to create a new DigestAuth
|
|
42
|
+
# object in the servlet's #initialize. By default WEBrick creates a new
|
|
43
|
+
# servlet instance for every request and the DigestAuth object must be
|
|
44
|
+
# used across requests.
|
|
45
|
+
|
|
46
|
+
class DigestAuth
|
|
47
|
+
include Authenticator
|
|
48
|
+
|
|
49
|
+
AuthScheme = "Digest" # :nodoc:
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
# Struct containing the opaque portion of the digest authentication
|
|
53
|
+
|
|
54
|
+
OpaqueInfo = Struct.new(:time, :nonce, :nc) # :nodoc:
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# Digest authentication algorithm
|
|
58
|
+
|
|
59
|
+
attr_reader :algorithm
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Quality of protection. RFC 2617 defines "auth" and "auth-int"
|
|
63
|
+
|
|
64
|
+
attr_reader :qop
|
|
65
|
+
|
|
66
|
+
##
|
|
67
|
+
# Used by UserDB to create a digest password entry
|
|
68
|
+
|
|
69
|
+
def self.make_passwd(realm, user, pass)
|
|
70
|
+
pass ||= ""
|
|
71
|
+
Digest::MD5::hexdigest([user, realm, pass].join(":"))
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
# Creates a new DigestAuth instance. Be sure to use the same DigestAuth
|
|
76
|
+
# instance for multiple requests as it saves state between requests in
|
|
77
|
+
# order to perform authentication.
|
|
78
|
+
#
|
|
79
|
+
# See WEBrick::Config::DigestAuth for default configuration entries
|
|
80
|
+
#
|
|
81
|
+
# You must supply the following configuration entries:
|
|
82
|
+
#
|
|
83
|
+
# :Realm:: The name of the realm being protected.
|
|
84
|
+
# :UserDB:: A database of usernames and passwords.
|
|
85
|
+
# A WEBrick::HTTPAuth::Htdigest instance should be used.
|
|
86
|
+
|
|
87
|
+
def initialize(config, default=Config::DigestAuth)
|
|
88
|
+
check_init(config)
|
|
89
|
+
@config = default.dup.update(config)
|
|
90
|
+
@algorithm = @config[:Algorithm]
|
|
91
|
+
@domain = @config[:Domain]
|
|
92
|
+
@qop = @config[:Qop]
|
|
93
|
+
@use_opaque = @config[:UseOpaque]
|
|
94
|
+
@use_next_nonce = @config[:UseNextNonce]
|
|
95
|
+
@check_nc = @config[:CheckNc]
|
|
96
|
+
@use_auth_info_header = @config[:UseAuthenticationInfoHeader]
|
|
97
|
+
@nonce_expire_period = @config[:NonceExpirePeriod]
|
|
98
|
+
@nonce_expire_delta = @config[:NonceExpireDelta]
|
|
99
|
+
@internet_explorer_hack = @config[:InternetExplorerHack]
|
|
100
|
+
|
|
101
|
+
case @algorithm
|
|
102
|
+
when 'MD5','MD5-sess'
|
|
103
|
+
@h = Digest::MD5
|
|
104
|
+
when 'SHA1','SHA1-sess' # it is a bonus feature :-)
|
|
105
|
+
@h = Digest::SHA1
|
|
106
|
+
else
|
|
107
|
+
msg = format('Algorithm "%s" is not supported.', @algorithm)
|
|
108
|
+
raise ArgumentError.new(msg)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
@instance_key = hexdigest(self.__id__, Time.now.to_i, Process.pid)
|
|
112
|
+
@opaques = {}
|
|
113
|
+
@last_nonce_expire = Time.now
|
|
114
|
+
@mutex = Thread::Mutex.new
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
##
|
|
118
|
+
# Authenticates a +req+ and returns a 401 Unauthorized using +res+ if
|
|
119
|
+
# the authentication was not correct.
|
|
120
|
+
|
|
121
|
+
def authenticate(req, res)
|
|
122
|
+
unless result = @mutex.synchronize{ _authenticate(req, res) }
|
|
123
|
+
challenge(req, res)
|
|
124
|
+
end
|
|
125
|
+
if result == :nonce_is_stale
|
|
126
|
+
challenge(req, res, true)
|
|
127
|
+
end
|
|
128
|
+
return true
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
##
|
|
132
|
+
# Returns a challenge response which asks for authentication information
|
|
133
|
+
|
|
134
|
+
def challenge(req, res, stale=false)
|
|
135
|
+
nonce = generate_next_nonce(req)
|
|
136
|
+
if @use_opaque
|
|
137
|
+
opaque = generate_opaque(req)
|
|
138
|
+
@opaques[opaque].nonce = nonce
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
param = Hash.new
|
|
142
|
+
param["realm"] = HTTPUtils::quote(@realm)
|
|
143
|
+
param["domain"] = HTTPUtils::quote(@domain.to_a.join(" ")) if @domain
|
|
144
|
+
param["nonce"] = HTTPUtils::quote(nonce)
|
|
145
|
+
param["opaque"] = HTTPUtils::quote(opaque) if opaque
|
|
146
|
+
param["stale"] = stale.to_s
|
|
147
|
+
param["algorithm"] = @algorithm
|
|
148
|
+
param["qop"] = HTTPUtils::quote(@qop.to_a.join(",")) if @qop
|
|
149
|
+
|
|
150
|
+
res[@response_field] =
|
|
151
|
+
"#{@auth_scheme} " + param.map{|k,v| "#{k}=#{v}" }.join(", ")
|
|
152
|
+
info("%s: %s", @response_field, res[@response_field]) if $DEBUG
|
|
153
|
+
raise @auth_exception
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
private
|
|
157
|
+
|
|
158
|
+
# :stopdoc:
|
|
159
|
+
|
|
160
|
+
MustParams = ['username','realm','nonce','uri','response']
|
|
161
|
+
MustParamsAuth = ['cnonce','nc']
|
|
162
|
+
|
|
163
|
+
def _authenticate(req, res)
|
|
164
|
+
unless digest_credentials = check_scheme(req)
|
|
165
|
+
return false
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
auth_req = split_param_value(digest_credentials)
|
|
169
|
+
if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
|
|
170
|
+
req_params = MustParams + MustParamsAuth
|
|
171
|
+
else
|
|
172
|
+
req_params = MustParams
|
|
173
|
+
end
|
|
174
|
+
req_params.each{|key|
|
|
175
|
+
unless auth_req.has_key?(key)
|
|
176
|
+
error('%s: parameter missing. "%s"', auth_req['username'], key)
|
|
177
|
+
raise HTTPStatus::BadRequest
|
|
178
|
+
end
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if !check_uri(req, auth_req)
|
|
182
|
+
raise HTTPStatus::BadRequest
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
if auth_req['realm'] != @realm
|
|
186
|
+
error('%s: realm unmatch. "%s" for "%s"',
|
|
187
|
+
auth_req['username'], auth_req['realm'], @realm)
|
|
188
|
+
return false
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
auth_req['algorithm'] ||= 'MD5'
|
|
192
|
+
if auth_req['algorithm'].upcase != @algorithm.upcase
|
|
193
|
+
error('%s: algorithm unmatch. "%s" for "%s"',
|
|
194
|
+
auth_req['username'], auth_req['algorithm'], @algorithm)
|
|
195
|
+
return false
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
if (@qop.nil? && auth_req.has_key?('qop')) ||
|
|
199
|
+
(@qop && (! @qop.member?(auth_req['qop'])))
|
|
200
|
+
error('%s: the qop is not allowed. "%s"',
|
|
201
|
+
auth_req['username'], auth_req['qop'])
|
|
202
|
+
return false
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
password = @userdb.get_passwd(@realm, auth_req['username'], @reload_db)
|
|
206
|
+
unless password
|
|
207
|
+
error('%s: the user is not allowed.', auth_req['username'])
|
|
208
|
+
return false
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
nonce_is_invalid = false
|
|
212
|
+
if @use_opaque
|
|
213
|
+
info("@opaque = %s", @opaque.inspect) if $DEBUG
|
|
214
|
+
if !(opaque = auth_req['opaque'])
|
|
215
|
+
error('%s: opaque is not given.', auth_req['username'])
|
|
216
|
+
nonce_is_invalid = true
|
|
217
|
+
elsif !(opaque_struct = @opaques[opaque])
|
|
218
|
+
error('%s: invalid opaque is given.', auth_req['username'])
|
|
219
|
+
nonce_is_invalid = true
|
|
220
|
+
elsif !check_opaque(opaque_struct, req, auth_req)
|
|
221
|
+
@opaques.delete(auth_req['opaque'])
|
|
222
|
+
nonce_is_invalid = true
|
|
223
|
+
end
|
|
224
|
+
elsif !check_nonce(req, auth_req)
|
|
225
|
+
nonce_is_invalid = true
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
if /-sess$/i =~ auth_req['algorithm']
|
|
229
|
+
ha1 = hexdigest(password, auth_req['nonce'], auth_req['cnonce'])
|
|
230
|
+
else
|
|
231
|
+
ha1 = password
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
if auth_req['qop'] == "auth" || auth_req['qop'] == nil
|
|
235
|
+
ha2 = hexdigest(req.request_method, auth_req['uri'])
|
|
236
|
+
ha2_res = hexdigest("", auth_req['uri'])
|
|
237
|
+
elsif auth_req['qop'] == "auth-int"
|
|
238
|
+
body_digest = @h.new
|
|
239
|
+
req.body { |chunk| body_digest.update(chunk) }
|
|
240
|
+
body_digest = body_digest.hexdigest
|
|
241
|
+
ha2 = hexdigest(req.request_method, auth_req['uri'], body_digest)
|
|
242
|
+
ha2_res = hexdigest("", auth_req['uri'], body_digest)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
|
|
246
|
+
param2 = ['nonce', 'nc', 'cnonce', 'qop'].map{|key|
|
|
247
|
+
auth_req[key]
|
|
248
|
+
}.join(':')
|
|
249
|
+
digest = hexdigest(ha1, param2, ha2)
|
|
250
|
+
digest_res = hexdigest(ha1, param2, ha2_res)
|
|
251
|
+
else
|
|
252
|
+
digest = hexdigest(ha1, auth_req['nonce'], ha2)
|
|
253
|
+
digest_res = hexdigest(ha1, auth_req['nonce'], ha2_res)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
if digest != auth_req['response']
|
|
257
|
+
error("%s: digest unmatch.", auth_req['username'])
|
|
258
|
+
return false
|
|
259
|
+
elsif nonce_is_invalid
|
|
260
|
+
error('%s: digest is valid, but nonce is not valid.',
|
|
261
|
+
auth_req['username'])
|
|
262
|
+
return :nonce_is_stale
|
|
263
|
+
elsif @use_auth_info_header
|
|
264
|
+
auth_info = {
|
|
265
|
+
'nextnonce' => generate_next_nonce(req),
|
|
266
|
+
'rspauth' => digest_res
|
|
267
|
+
}
|
|
268
|
+
if @use_opaque
|
|
269
|
+
opaque_struct.time = req.request_time
|
|
270
|
+
opaque_struct.nonce = auth_info['nextnonce']
|
|
271
|
+
opaque_struct.nc = "%08x" % (auth_req['nc'].hex + 1)
|
|
272
|
+
end
|
|
273
|
+
if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
|
|
274
|
+
['qop','cnonce','nc'].each{|key|
|
|
275
|
+
auth_info[key] = auth_req[key]
|
|
276
|
+
}
|
|
277
|
+
end
|
|
278
|
+
res[@resp_info_field] = auth_info.keys.map{|key|
|
|
279
|
+
if key == 'nc'
|
|
280
|
+
key + '=' + auth_info[key]
|
|
281
|
+
else
|
|
282
|
+
key + "=" + HTTPUtils::quote(auth_info[key])
|
|
283
|
+
end
|
|
284
|
+
}.join(', ')
|
|
285
|
+
end
|
|
286
|
+
info('%s: authentication succeeded.', auth_req['username'])
|
|
287
|
+
req.user = auth_req['username']
|
|
288
|
+
return true
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def split_param_value(string)
|
|
292
|
+
ret = {}
|
|
293
|
+
string.scan(/\G\s*([\w\-.*%!]+)=\s*(?:\"((?>\\.|[^\"])*)\"|([^,\"]*))\s*,?/) do
|
|
294
|
+
ret[$1] = $3 || $2.gsub(/\\(.)/, "\\1")
|
|
295
|
+
end
|
|
296
|
+
ret
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def generate_next_nonce(req)
|
|
300
|
+
now = "%012d" % req.request_time.to_i
|
|
301
|
+
pk = hexdigest(now, @instance_key)[0,32]
|
|
302
|
+
nonce = [now + ":" + pk].pack("m0") # it has 60 length of chars.
|
|
303
|
+
nonce
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def check_nonce(req, auth_req)
|
|
307
|
+
username = auth_req['username']
|
|
308
|
+
nonce = auth_req['nonce']
|
|
309
|
+
|
|
310
|
+
pub_time, pk = nonce.unpack("m*")[0].split(":", 2)
|
|
311
|
+
if (!pub_time || !pk)
|
|
312
|
+
error("%s: empty nonce is given", username)
|
|
313
|
+
return false
|
|
314
|
+
elsif (hexdigest(pub_time, @instance_key)[0,32] != pk)
|
|
315
|
+
error("%s: invalid private-key: %s for %s",
|
|
316
|
+
username, hexdigest(pub_time, @instance_key)[0,32], pk)
|
|
317
|
+
return false
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
diff_time = req.request_time.to_i - pub_time.to_i
|
|
321
|
+
if (diff_time < 0)
|
|
322
|
+
error("%s: difference of time-stamp is negative.", username)
|
|
323
|
+
return false
|
|
324
|
+
elsif diff_time > @nonce_expire_period
|
|
325
|
+
error("%s: nonce is expired.", username)
|
|
326
|
+
return false
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
return true
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def generate_opaque(req)
|
|
333
|
+
@mutex.synchronize{
|
|
334
|
+
now = req.request_time
|
|
335
|
+
if now - @last_nonce_expire > @nonce_expire_delta
|
|
336
|
+
@opaques.delete_if{|key,val|
|
|
337
|
+
(now - val.time) > @nonce_expire_period
|
|
338
|
+
}
|
|
339
|
+
@last_nonce_expire = now
|
|
340
|
+
end
|
|
341
|
+
begin
|
|
342
|
+
opaque = Utils::random_string(16)
|
|
343
|
+
end while @opaques[opaque]
|
|
344
|
+
@opaques[opaque] = OpaqueInfo.new(now, nil, '00000001')
|
|
345
|
+
opaque
|
|
346
|
+
}
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def check_opaque(opaque_struct, req, auth_req)
|
|
350
|
+
if (@use_next_nonce && auth_req['nonce'] != opaque_struct.nonce)
|
|
351
|
+
error('%s: nonce unmatched. "%s" for "%s"',
|
|
352
|
+
auth_req['username'], auth_req['nonce'], opaque_struct.nonce)
|
|
353
|
+
return false
|
|
354
|
+
elsif !check_nonce(req, auth_req)
|
|
355
|
+
return false
|
|
356
|
+
end
|
|
357
|
+
if (@check_nc && auth_req['nc'] != opaque_struct.nc)
|
|
358
|
+
error('%s: nc unmatched."%s" for "%s"',
|
|
359
|
+
auth_req['username'], auth_req['nc'], opaque_struct.nc)
|
|
360
|
+
return false
|
|
361
|
+
end
|
|
362
|
+
true
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def check_uri(req, auth_req)
|
|
366
|
+
uri = auth_req['uri']
|
|
367
|
+
if uri != req.request_uri.to_s && uri != req.unparsed_uri &&
|
|
368
|
+
(@internet_explorer_hack && uri != req.path)
|
|
369
|
+
error('%s: uri unmatch. "%s" for "%s"', auth_req['username'],
|
|
370
|
+
auth_req['uri'], req.request_uri.to_s)
|
|
371
|
+
return false
|
|
372
|
+
end
|
|
373
|
+
true
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def hexdigest(*args)
|
|
377
|
+
@h.hexdigest(args.join(":"))
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
# :startdoc:
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
##
|
|
384
|
+
# Digest authentication for proxy servers. See DigestAuth for details.
|
|
385
|
+
|
|
386
|
+
class ProxyDigestAuth < DigestAuth
|
|
387
|
+
include ProxyAuthenticator
|
|
388
|
+
|
|
389
|
+
private
|
|
390
|
+
def check_uri(req, auth_req) # :nodoc:
|
|
391
|
+
return true
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
end
|
|
@@ -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
|