clarion 0.1.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.
@@ -0,0 +1,51 @@
1
+ <style type="text/css">
2
+ #procession > div {
3
+ display: none;
4
+ }
5
+ #procession.procession_init > div.procession_init {
6
+ display: block;
7
+ }
8
+ #procession.procession_unsupported > div.procession_unsupported {
9
+ display: block;
10
+ }
11
+ #procession.procession_wait > div.procession_wait {
12
+ display: block;
13
+ }
14
+ #procession.procession_contact > div.procession_contact {
15
+ display: block;
16
+ }
17
+ #procession.procession_ok > div.procession_ok {
18
+ display: block;
19
+ }
20
+ #procession.procession_error > div.procession_error {
21
+ display: block;
22
+ }
23
+ </style>
24
+
25
+ <p class='center'><strong>U2F 2FA <%- if @authn.name -%> for <%= @authn.name %><%- end -%></strong></p>
26
+ <div id="procession" class="procession_init" data-authn-id="<%= @authn.id %>" data-app-id="<%= @app_id %>" data-requests='<%= @requests.to_json %>' data-challenge='<%= @challenge.to_json %>' data-req-id='<%= @req_id %>'>
27
+ <div class="procession_init">
28
+ <p>Initializing...</p>
29
+ </div>
30
+ <div class="procession_unsupported">
31
+ <p>You have to use browser supporting FIDO U2F</p>
32
+ </div>
33
+ <div class="procession_wait">
34
+ <p>Insert and tap your security key.</p>
35
+ </div>
36
+ <div class="procession_contact">
37
+ <p>Contacting...</p>
38
+ </div>
39
+ <div class="procession_ok">
40
+ <p>OK: You may now close this page.</p>
41
+ </div>
42
+ <div class="procession_error">
43
+ <p>Error: Reload and try again?</p>
44
+ </div>
45
+ </div>
46
+ <%- if @authn.comment -%>
47
+ <p><small><%= @authn.comment %></small></p>
48
+ <%- end -%>
49
+
50
+
51
+ <script src="/sign.js"></script>
@@ -0,0 +1,128 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset='utf-8'>
5
+ <title>Clarion</title>
6
+ <style type="text/css">
7
+ * {
8
+ font-family: 'Avenir Next', sans-serif;
9
+ font-size: 16px;
10
+ }
11
+ pre, code {
12
+ font-family: 'Monaco', monospace;
13
+ }
14
+ pre, pre code {
15
+ width: 100%;
16
+ white-space: pre-wrap;
17
+ word-wrap: break-word;
18
+ font-size: 14px;
19
+ }
20
+ strong {
21
+ font-weight: bold;
22
+ }
23
+ small {
24
+ font-size: 12px;
25
+ }
26
+
27
+ .center {
28
+ text-align: center;
29
+ }
30
+
31
+ body {
32
+ text-align: center;
33
+ background-color: white;
34
+ }
35
+
36
+ .hidden {
37
+ display: none;
38
+ }
39
+
40
+ .container {
41
+ margin-left: auto;
42
+ margin-right: auto;
43
+ width: 400px;
44
+ text-align: left;
45
+ padding-top: 12px;
46
+ }
47
+
48
+ .box, .form, .notice, .error {
49
+ border-radius: 3px;
50
+ margin-bottom: 12px;
51
+ padding: 8px 20px;
52
+ }
53
+
54
+ .box {
55
+ padding: 12px 20px;
56
+ border: 1px solid #ddd;
57
+ background-color: white;
58
+ }
59
+
60
+ .notice {
61
+ border: 1px solid #D6E9C6;
62
+ background-color: #DFF0D8;
63
+ color: #3C763D;
64
+ }
65
+
66
+ .error {
67
+ border: 1px solid #EBCCD1;
68
+ background-color: #F2DEDE;
69
+ color: #A94442;
70
+ }
71
+
72
+
73
+ .box input, .box button {
74
+ width: 100%;
75
+ -moz-box-sizing: border-box;
76
+ -webkit-box-sizing: border-box;
77
+ box-sizing: border-box;
78
+
79
+ font-size: 20px;
80
+ padding: 6px 4px;
81
+
82
+ border: 1px solid #e9e9e9;
83
+ border-radius: 3px;
84
+ }
85
+
86
+ input[type="submit"], button {
87
+ background-color: #337AB7;
88
+ color: white;
89
+ }
90
+
91
+ input[type="submit"]:hover, button:hover {
92
+ background-color: #286090;
93
+ }
94
+
95
+ .credit {
96
+ text-align: center;
97
+ }
98
+
99
+ .credit, .credit * {
100
+ color: #767676;
101
+ font-size: 12px;
102
+ }
103
+ </style>
104
+ <script src="/u2f-api.js"></script>
105
+ </head>
106
+
107
+ <body>
108
+ <div class="container">
109
+ <% notice ||= nil; error ||= nil %>
110
+ <% if notice %>
111
+ <div class="notice"><%= notice %></div>
112
+ <% end %>
113
+
114
+ <% if error %>
115
+ <div class="error"><%= error %></div>
116
+ <% end %>
117
+
118
+ <div class="box">
119
+ <%== yield %>
120
+ </div>
121
+
122
+ <div class="credit">
123
+ Powered by <a href="https://github.com/sorah/clarion">sorah/clarion</a>
124
+ </div>
125
+ </div>
126
+
127
+ <body>
128
+ </body>
@@ -0,0 +1,55 @@
1
+ <style type="text/css">
2
+ #procession > div {
3
+ display: none;
4
+ }
5
+ #procession.procession_init > div.procession_init {
6
+ display: block;
7
+ }
8
+ #procession.procession_unsupported > div.procession_unsupported {
9
+ display: block;
10
+ }
11
+ #procession.procession_wait > div.procession_wait {
12
+ display: block;
13
+ }
14
+ #procession.procession_contact > div.procession_contact {
15
+ display: block;
16
+ }
17
+ #procession.procession_ok > div.procession_ok {
18
+ display: block;
19
+ }
20
+ #procession.procession_error > div.procession_error {
21
+ display: block;
22
+ }
23
+ </style>
24
+
25
+ <p><strong>U2F key registration<%- if @name -%> for <%= @name %><%- end -%></strong></p>
26
+ <div id="procession" class="procession_init" data-app-id="<%= @app_id %>" data-requests='<%= @requests.to_json %>' data-state='<%= @state %>' data-callback='<%= @callback %>' data-reg-id='<%= @reg_id %>'>
27
+ <form id="callback_form" class="hidden" method='POST'>
28
+ <input type="hidden" name="state" value="<%= @state %>">
29
+ <input type="hidden" name="data" value="">
30
+ </form>
31
+ <div class="procession_init">
32
+ <p>Loading...</p>
33
+ </div>
34
+ <div class="procession_unsupported">
35
+ <p>You have to use browser supporting FIDO U2F</p>
36
+ </div>
37
+ <div class="procession_wait">
38
+ <p>Insert and tap your security key.</p>
39
+ </div>
40
+ <div class="procession_contact">
41
+ <p>Contacting...</p>
42
+ </div>
43
+ <div class="procession_ok">
44
+ <p>OK...</p>
45
+ </div>
46
+ <div class="procession_error">
47
+ <p>Error: try again from the previous page?</p>
48
+ </div>
49
+ </div>
50
+ <%- if @comment -%>
51
+ <p><small><%= @comment %></small></p>
52
+ <%- end -%>
53
+
54
+
55
+ <script src="/register.js"></script>
@@ -0,0 +1,35 @@
1
+ <form action="<%= @register_url %>" method="POST">
2
+ <input type="hidden" name="name" value="<%= @name %>">
3
+ <input type="hidden" name="comment" value="<%= @comment %>">
4
+ <input type="hidden" name="state" value="<%= @state%>">
5
+ <input type="hidden" name="callback" value="<%= @callback %>">
6
+ <input type="hidden" name="public_key" value="<%= @public_key %>">
7
+ <input type="submit" value="Register (POST)">
8
+ </form>
9
+
10
+ <form id="callback_form" class="hidden" method='POST' action="/test/callback">
11
+ <input type="hidden" name="state" value="">
12
+ <input type="hidden" name="data" value="">
13
+ </form>
14
+
15
+ <button id="register_cb_button" data-url="/register?<%= URI.encode_www_form(name: @name, comment: @comment, state: @state, callback: "js:#{request.base_url}", public_key: @public_key)%>">Register (JS callback)</button>
16
+ <script>
17
+ "use strict";
18
+ document.addEventListener("DOMContentLoaded", function() {
19
+ window.addEventListener("message", function(event) {
20
+ console.log(event.data);
21
+ if (event.data.clarion_key) {
22
+ let data = event.data.clarion_key;
23
+ let form = document.getElementById("callback_form");
24
+ form.querySelector('[name=state]').value = data.state;
25
+ form.querySelector('[name=data]').value = data.data;
26
+ form.submit();
27
+ }
28
+ }, false);
29
+
30
+ let button = document.getElementById("register_cb_button");
31
+ button.addEventListener("click", function() {
32
+ window.open(button.attributes['data-url'].value, '_blank');
33
+ });
34
+ });
35
+ </script>
@@ -0,0 +1,10 @@
1
+ <pre><code><%= @key.to_json(:all) %></code></pre>
2
+
3
+ <button id="start_authn_button">Create Authn</button>
4
+ <button class='hidden' id="open_authn_button">Open Authn</button>
5
+ <pre>
6
+ <code id="authn_test" data-key='<%= @key.to_json(:all) %>'>
7
+ </code>
8
+ </pre>
9
+
10
+ <script src="/test.js"></script>
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "clarion"
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(__FILE__)
@@ -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,11 @@
1
+ #!/usr/bin/env ruby
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'uri'
5
+
6
+ base = ARGV[0]
7
+ unless base
8
+ abort "Usage: #{$0} url"
9
+ end
10
+
11
+
@@ -0,0 +1,32 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "clarion/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "clarion"
8
+ spec.version = Clarion::VERSION
9
+ spec.authors = ["Sorah Fukumori"]
10
+ spec.email = ["sorah@cookpad.com"]
11
+
12
+ spec.summary = %q{Web-based FIDO U2F Helper for CLI operations (SSH login...)}
13
+ spec.homepage = "https://github.com/sorah/clarion"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency "u2f"
24
+ spec.add_dependency "sinatra"
25
+ spec.add_dependency "erubis"
26
+ spec.add_dependency "aws-sdk-s3"
27
+ spec.add_dependency "aws-sdk-dynamodb"
28
+
29
+ spec.add_development_dependency "bundler"
30
+ spec.add_development_dependency "rake"
31
+ spec.add_development_dependency "rspec", "~> 3.0"
32
+ end
@@ -0,0 +1,63 @@
1
+ require 'bundler/setup'
2
+ require 'securerandom'
3
+
4
+ require 'clarion'
5
+
6
+ if ENV['RACK_ENV'] == 'production'
7
+ raise 'Set $SECRET_KEY_BASE' unless ENV['SECRET_KEY_BASE']
8
+ end
9
+
10
+ if ENV['CLARION_DEV_HTTPS']
11
+ use(Class.new do
12
+ def initialize(app)
13
+ @app = app
14
+ end
15
+ def call(env)
16
+ @app.call env.merge('HTTPS' => 'on')
17
+ end
18
+ end)
19
+ end
20
+
21
+ config = {
22
+ registration_allowed_url: Regexp.new(ENV.fetch('CLARION_REGISTRATION_ALLOWED_URL')),
23
+ authn_default_expires_in: ENV.fetch('CLARION_AUTHN_DEFAULT_EXPIRES_IN', 300).to_i,
24
+ }
25
+
26
+ case ENV.fetch('CLARION_STORE', 's3')
27
+ when 's3'
28
+ config[:store] = {
29
+ kind: :s3,
30
+ region: ENV.fetch('CLARION_STORE_S3_REGION'),
31
+ bucket: ENV.fetch('CLARION_STORE_S3_BUCKET'),
32
+ prefix: ENV.fetch('CLARION_STORE_S3_PREFIX'),
33
+ }
34
+ when 'memory'
35
+ config[:store] = {kind: :memory}
36
+ else
37
+ raise ArgumentError, "Unsupported $CLARION_STORE"
38
+ end
39
+
40
+ case ENV.fetch('CLARION_COUNTER', nil)
41
+ when 'dynamodb'
42
+ config[:counter] = {
43
+ kind: :dynamodb,
44
+ region: ENV.fetch('CLARION_COUNTER_DYNAMODB_REGION'),
45
+ table_name: ENV.fetch('CLARION_COUNTER_DYNAMODB_TABLE'),
46
+ }
47
+ when 'memory'
48
+ config[:counter] = {kind: :memory}
49
+ when nil
50
+ # do nothing
51
+ else
52
+ raise ArgumentError, "Unsupported $CLARION_COUNTER"
53
+ end
54
+
55
+ use(
56
+ Rack::Session::Cookie,
57
+ key: 'clarionsess',
58
+ expire_after: 3600,
59
+ secure: ENV['RACK_ENV'] == 'production',
60
+ secret: ENV.fetch('SECRET_KEY_BASE', SecureRandom.base64(256)),
61
+ )
62
+
63
+ run Clarion.app(Clarion::Config.new(config))
data/dev.rb ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ require 'bundler/setup'
3
+ require 'webrick'
4
+ require 'webrick/ssl'
5
+ require 'logger'
6
+ require 'rack'
7
+ ENV['RACK_ENV'] = 'development'
8
+ ENV['CLARION_DEV_HTTPS']='1'
9
+ ENV['CLARION_REGISTRATION_ALLOWED_URL']='.+'
10
+
11
+ ENV['CLARION_STORE']='memory'
12
+ ENV['CLARION_COUNTER']='memory'
13
+
14
+
15
+ app, _ = Rack::Builder.parse_file(File.join(__dir__, 'config.ru'))
16
+
17
+ rack_options = {
18
+ app: app,
19
+ Port: ENV.fetch('PORT', 3000).to_i,
20
+ Host: ENV.fetch('BIND', '127.0.0.1'),
21
+ environment: 'development',
22
+ server: 'webrick',
23
+ Logger: Logger.new($stdout),#.tap { |_| _.level = Logger::DEBUG },
24
+ SSLEnable: true,
25
+ SSLCertName: 'CN=localhost',
26
+ SSLCertComment: 'dummy',
27
+ SSLStartImmediately: true,
28
+ }
29
+ server = Rack::Server.new(rack_options)
30
+ server.start