clarion 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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