faceauth 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +171 -0
- data/Rakefile +37 -0
- data/app/assets/config/faceauth_manifest.js +2 -0
- data/app/assets/images/faceauth/loader.gif +0 -0
- data/app/assets/javascripts/faceauth/application.js +15 -0
- data/app/assets/javascripts/faceauth/faces.js +177 -0
- data/app/assets/javascripts/faceauth/jpeg_camera.swf +0 -0
- data/app/assets/javascripts/faceauth/jpeg_camera_with_dependencies.min.js +12 -0
- data/app/assets/javascripts/faceauth/shutter.mp3 +0 -0
- data/app/assets/javascripts/faceauth/shutter.ogg +0 -0
- data/app/assets/stylesheets/faceauth/application.css +17 -0
- data/app/assets/stylesheets/faceauth/faces.css +59 -0
- data/app/controllers/faceauth/application_controller.rb +7 -0
- data/app/controllers/faceauth/faces_controller.rb +44 -0
- data/app/helpers/faceauth/application_helper.rb +4 -0
- data/app/helpers/faceauth/faces_helper.rb +4 -0
- data/app/jobs/faceauth/application_job.rb +4 -0
- data/app/mailers/faceauth/application_mailer.rb +6 -0
- data/app/models/faceauth/application_record.rb +5 -0
- data/app/views/faceauth/faces/new.html.erb +22 -0
- data/app/views/layouts/faceauth/application.html.erb +23 -0
- data/config/routes.rb +4 -0
- data/lib/faceauth.rb +17 -0
- data/lib/faceauth/authenticate.rb +26 -0
- data/lib/faceauth/configuration.rb +28 -0
- data/lib/faceauth/engine.rb +12 -0
- data/lib/faceauth/gem_dependencies.rb +3 -0
- data/lib/faceauth/version.rb +3 -0
- data/lib/generators/faceauth/install_generator.rb +19 -0
- data/lib/generators/faceauth/views_generator.rb +34 -0
- data/lib/generators/templates/README +34 -0
- data/lib/generators/templates/initializer.rb +10 -0
- data/lib/tasks/faceauth_tasks.rake +4 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9eb5abab2e9f52d4ecd2b71874e89ca0071a9b92
|
4
|
+
data.tar.gz: 18786ea1f88e9a7bfc24af02c18d5d19ac67d5f3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bc86ec5a7bbf4755e81aca514a818b5e2156f160db80353f1c88793c1bdf926f7dd577414ee571f96397bf7bfa6b63ff494b42792a017ab24f3be71f752ed69c
|
7
|
+
data.tar.gz: 0ada40a7ffd82f5cfc1092740c2722a791f284f8fea6da13000538dd84d10e2b76edf8a1197ef5e01dad3be97f18e1a7a6119831a7e2681c018d191005380115
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017 Sam
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
# Faceauth
|
2
|
+
Faceauth gem is a simple plugin which allows users to sign into any rails based web application using their face. Primarily supporting devise authentication system (Please refer [Devise gem](https://github.com/plataformatec/devise)), this gem uses [FindFace Cloud API](https://findface.pro/en/) to run the matches & authenticate users. This is implemented from thought process of finding new ways of authenticating users eliminating need for users to remember so many passwords for various web applications they interact with.
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'faceauth'
|
9
|
+
```
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
```bash
|
13
|
+
$ bundle
|
14
|
+
```
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
```bash
|
18
|
+
$ gem install faceauth
|
19
|
+
```
|
20
|
+
|
21
|
+
Next, you need to run the generator:
|
22
|
+
```bash
|
23
|
+
$ rails generate faceauth:install
|
24
|
+
```
|
25
|
+
|
26
|
+
|
27
|
+
## Embedding in a Rails app
|
28
|
+
|
29
|
+
Add the following to your routes.rb file:
|
30
|
+
|
31
|
+
``` ruby
|
32
|
+
mount Faceauth::Engine, at: "/"
|
33
|
+
```
|
34
|
+
|
35
|
+
(**Note:** You may mount the server at any path, not just at "/")
|
36
|
+
|
37
|
+
Add two columns using migration to your devise user model in rails application. Example for column names: user_picture, last_sign_in_picture
|
38
|
+
|
39
|
+
And run migrations:
|
40
|
+
|
41
|
+
```bash
|
42
|
+
$ rake db:migrate
|
43
|
+
```
|
44
|
+
|
45
|
+
Generate devise views: (Assumption that you are already using devise gem for authentication platform in your rails application)
|
46
|
+
|
47
|
+
```bash
|
48
|
+
rails generate devise:views
|
49
|
+
```
|
50
|
+
|
51
|
+
add the following in devise/shared/_links.html.erb file.
|
52
|
+
|
53
|
+
``` erb
|
54
|
+
<%= link_to "Log in using your face", faceauth.new_session_path %><br />
|
55
|
+
```
|
56
|
+
(**Note:** Ensure to setup process of recording user picture during signup process and store details in :user_picture column.)
|
57
|
+
|
58
|
+
Next, setup the configuration:
|
59
|
+
|
60
|
+
Open your initializers file at config/initializers/faceauth.rb where you can configure settings for faceauth as follows:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
Faceauth.configuration do |config|
|
64
|
+
config.model_name = "user"
|
65
|
+
config.uploader_name = "carrierwave"
|
66
|
+
config.redirect_url = "root_path"
|
67
|
+
config.findface_api_key = "YOUR_API_KEY"
|
68
|
+
config.signup_picture_column = "user_picture"
|
69
|
+
config.signin_picture_column = "last_sign_in_picture"
|
70
|
+
config.email_column = "email"
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
The options available are:
|
75
|
+
|
76
|
+
| Option | Description | Default Values |
|
77
|
+
| ----------------- | ------------------------------- | -------------------------------- |
|
78
|
+
| `model_name` | String to set devise user model . | "user" |
|
79
|
+
| `uploader_name` | String to define attachments plugin used to store images. | "carrierwave" or "paperclip" |
|
80
|
+
| `redirect_url` | String to provide location details for taking user after successful authentication. | "root_path" |
|
81
|
+
| `findface_api_key` | String to set Findface API Key. | "Findface API key" |
|
82
|
+
| `email_column` | set column name where user email is stored | "email" |
|
83
|
+
| `signup_picture_column` | set column name where user picture is stored | "user_picture" |
|
84
|
+
| `signin_picture_column` | set column name where user picture is stored recorded from faceauth sign in page | "last_sign_in_picture" |
|
85
|
+
|
86
|
+
|
87
|
+
Ensure you have set 'findface_api_key' and 'redirect_url' in config/initializers/faceauth.rb
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
config.findface_api_key = "YOUR_API_KEY"
|
91
|
+
config.redirect_url = "root_path" #By default, the option takes root_path of your rails application.
|
92
|
+
```
|
93
|
+
If you wish to pass your custom model name & column names, please set the names using
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
config.model_name = "your_model_name"
|
97
|
+
config.signup_picture_column = "custom_column_name"
|
98
|
+
config.signin_picture_column = "custom_column_name"
|
99
|
+
```
|
100
|
+
|
101
|
+
Start using the gem 🙂
|
102
|
+
|
103
|
+
|
104
|
+
**Note:**
|
105
|
+
|
106
|
+
In development environment please use [ngrok](https://ngrok.com/) with https protocol. As per the security standards & protocols access to microphones & webcames are allowed only through a secure channel.
|
107
|
+
|
108
|
+
## Customization
|
109
|
+
|
110
|
+
We built Faceuth to help you quickly integrate contact form in your application. However, we don't want to be in your way when you need to customize it.
|
111
|
+
|
112
|
+
Since Faceauth is an Rails engine, all its views are packaged inside the gem. These views will help you get started, but after some time you may want to change them. If this is the case, you just need to invoke the following generator, and it will copy all views to your application:
|
113
|
+
|
114
|
+
```console
|
115
|
+
$ rails generate faceauth:views
|
116
|
+
```
|
117
|
+
|
118
|
+
If you would like to generate only a few sets of views, like the ones for the `form`
|
119
|
+
you can pass a list of names to the generator with the `-v` flag.
|
120
|
+
|
121
|
+
```console
|
122
|
+
$ rails generate faceuath:views -v form
|
123
|
+
```
|
124
|
+
|
125
|
+
## Todos
|
126
|
+
|
127
|
+
1. Extending this functionality for other authentication plugins.
|
128
|
+
2. Support for ruby versions < 2.2.2 .
|
129
|
+
3. Writing Test cases.
|
130
|
+
4. Making signin process faster. i.e. The response time of Jpeg_camera gem we use to capture picture is slow. Need to make it faster such that it improves overall user experience.
|
131
|
+
5. User will be able to login by showing a hardcopy of photo instead of showing his face to webcam which allows successful authentication. Need to explore on this to avoid this scneario to make it more efficient and authentic.
|
132
|
+
6. Need to check system behaviour in assuming Twins in users base and find fixes for any issues observed.
|
133
|
+
7. Adding security question system will eliminate the use cases 5 & 6 after immediate positive verification, however this addition should not hammer user experience. Need to design very intutive & flexible user sign in flow.
|
134
|
+
|
135
|
+
|
136
|
+
## Contributing
|
137
|
+
|
138
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/faceauth. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
139
|
+
|
140
|
+
Please ensure to follow the following steps as standard practice to contribute:
|
141
|
+
|
142
|
+
1. Fork it ( https://github.com/rubyeffect/faceauth/fork )
|
143
|
+
2. Create your feature branch (git checkout -b my-new-feature)
|
144
|
+
3. Commit your changes (git commit -am 'Add some feature')
|
145
|
+
4. Push to the branch (git push origin my-new-feature)
|
146
|
+
5. Create a new Pull Request
|
147
|
+
|
148
|
+
|
149
|
+
## License
|
150
|
+
|
151
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
152
|
+
|
153
|
+
## Authored by
|
154
|
+
|
155
|
+
Sandeep Mallela a.k.a Sam (sandeep@rubyeffect.com)
|
156
|
+
|
157
|
+
**Contributions:**
|
158
|
+
|
159
|
+
Venkatesh Vasamasetti (venkatesh@rubyeffect.com)
|
160
|
+
|
161
|
+
## About RubyEffect
|
162
|
+
|
163
|
+
<a href="http://www.rubyeffect.com" target="_blank">
|
164
|
+
<img src="http://blog.rubyeffect.com/wp-content/uploads/2015/05/cropped-re_original_logo.png" alt="RubyEffect">
|
165
|
+
</a>
|
166
|
+
|
167
|
+
RubyEffect builds intuitive, live and elegant software that solves real world problems. We love open source and it's community.
|
168
|
+
|
169
|
+
Liked this gem? You may also like the articles we post on our [blog](http://blog.rubyeffect.com). Please do check.
|
170
|
+
|
171
|
+
We would love to work on your ideas and see them grow. Say hello @ http://rubyeffect.com/contact
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Faceauth'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
require 'bundler/gem_tasks'
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
task default: :test
|
Binary file
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
|
13
|
+
//= require jquery
|
14
|
+
//= require jquery_ujs
|
15
|
+
//= require_tree .
|
@@ -0,0 +1,177 @@
|
|
1
|
+
// Place all the behaviors and hooks related to the matching controller here.
|
2
|
+
// All this logic will automatically be available in application.js.
|
3
|
+
|
4
|
+
|
5
|
+
$(function() {
|
6
|
+
if (window.JpegCamera) {
|
7
|
+
var camera; // Initialized at the end
|
8
|
+
var update_stream_stats = function(stats) {
|
9
|
+
$("#stream_stats").html("Mean luminance = " + stats.mean + "; Standard Deviation = " + stats.std);
|
10
|
+
setTimeout(function() { camera.get_stats(update_stream_stats); }, 1000);
|
11
|
+
};
|
12
|
+
var take_snapshots = function(count) {
|
13
|
+
$("#discard_snapshot").show();
|
14
|
+
var snapshot = camera.capture();
|
15
|
+
if (JpegCamera.canvas_supported()) {
|
16
|
+
snapshot.get_canvas(add_snapshot);
|
17
|
+
|
18
|
+
} else {
|
19
|
+
// <canvas> is not supported in this browser. We'll use anonymous
|
20
|
+
// graphic instead.
|
21
|
+
var image = document.createElement("img");
|
22
|
+
image.src = "no_canvas_photo.jpg";
|
23
|
+
setTimeout(function() { add_snapshot.call(snapshot, image) }, 1);
|
24
|
+
$("#discard_snapshot").hide();
|
25
|
+
}
|
26
|
+
if (count > 1) {
|
27
|
+
setTimeout(function() { take_snapshots(count - 1); }, 500);
|
28
|
+
}
|
29
|
+
};
|
30
|
+
|
31
|
+
var add_snapshot = function(element) {
|
32
|
+
$(element).data("snapshot", this).addClass("item");
|
33
|
+
var $container = $("#snapshots").append(element);
|
34
|
+
var $camera = $("#camera");
|
35
|
+
var camera_ratio = $camera.innerWidth() / $camera.innerHeight();
|
36
|
+
var height = $container.height()
|
37
|
+
element.style.height = "" + height + "px";
|
38
|
+
element.style.width = "" + Math.round(camera_ratio * height) + "px";
|
39
|
+
var scroll = $container[0].scrollWidth - $container.innerWidth();
|
40
|
+
$container.animate({
|
41
|
+
scrollLeft: scroll
|
42
|
+
}, 200);
|
43
|
+
};
|
44
|
+
|
45
|
+
var select_snapshot = function() {
|
46
|
+
$(".item").removeClass("selected");
|
47
|
+
var snapshot = $(this).addClass("selected").data("snapshot");
|
48
|
+
//$("#discard_snapshot, #upload_snapshot, #api_url").show();
|
49
|
+
snapshot.show();
|
50
|
+
$("#show_stream").show();
|
51
|
+
};
|
52
|
+
|
53
|
+
var clear_upload_data = function() {
|
54
|
+
$("#upload_status, #upload_result").html("");
|
55
|
+
};
|
56
|
+
|
57
|
+
var upload_snapshot = function() {
|
58
|
+
var api_url = "/faces";
|
59
|
+
clear_upload_data();
|
60
|
+
$("#loader").show();
|
61
|
+
$("#upload_snapshot").prop("disabled", true);
|
62
|
+
var snapshot = $(".item.selected").data("snapshot");
|
63
|
+
email_value = $("#user_email").val();
|
64
|
+
if (email_value != undefined && isEmail(email_value)) {
|
65
|
+
if (snapshot != undefined) {
|
66
|
+
$("#upload_loader").show();
|
67
|
+
snapshot.upload({ api_url: api_url + "?email=" + email_value }).done(upload_done).fail(upload_fail);
|
68
|
+
} else {
|
69
|
+
$("#upload_loader").hide();
|
70
|
+
generate_validation_messages("Snapshot is missing! Please take a snap & try again");
|
71
|
+
}
|
72
|
+
} else {
|
73
|
+
$("#upload_loader").hide();
|
74
|
+
generate_validation_messages("Email is not invalid.");
|
75
|
+
}
|
76
|
+
};
|
77
|
+
|
78
|
+
var upload_done = function(response) {
|
79
|
+
resp = JSON.parse(response);
|
80
|
+
$("#upload_snapshot").prop("disabled", false);
|
81
|
+
$("#loader").hide();
|
82
|
+
// $("#upload_status").html("Status:");
|
83
|
+
if (resp["status"] == "success") {
|
84
|
+
window.location.replace(resp["location"]);
|
85
|
+
} else {
|
86
|
+
$("#upload_result").html(resp["message"]);
|
87
|
+
$("#upload_loader").hide();
|
88
|
+
$("#upload_result").addClass("alert alert-danger");
|
89
|
+
}
|
90
|
+
|
91
|
+
hide_alerts();
|
92
|
+
};
|
93
|
+
|
94
|
+
var upload_fail = function(code, error, response) {
|
95
|
+
$("#upload_snapshot").prop("disabled", false);
|
96
|
+
$("#loader").hide();
|
97
|
+
$("#upload_status").html("Upload failed with status " + code + " (" + error + ")");
|
98
|
+
$("#upload_result").html(response);
|
99
|
+
$("#upload_loader").hide();
|
100
|
+
};
|
101
|
+
|
102
|
+
var discard_snapshot = function() {
|
103
|
+
var element = $(".item.selected").removeClass("item selected");
|
104
|
+
if (element.data("snapshot") != undefined) {
|
105
|
+
var next = element.nextAll(".item").first();
|
106
|
+
if (!next.size()) {
|
107
|
+
next = element.prevAll(".item").first();
|
108
|
+
}
|
109
|
+
if (next.size()) {
|
110
|
+
next.addClass("selected");
|
111
|
+
next.data("snapshot").show();
|
112
|
+
} else {
|
113
|
+
hide_snapshot_controls();
|
114
|
+
}
|
115
|
+
element.data("snapshot").discard();
|
116
|
+
element.hide("slow", function() { $(this).remove() });
|
117
|
+
} else {
|
118
|
+
generate_validation_messages("Please select a snapshot to discard.");
|
119
|
+
}
|
120
|
+
|
121
|
+
};
|
122
|
+
|
123
|
+
var show_stream = function() {
|
124
|
+
$(this).hide();
|
125
|
+
$(".item").removeClass("selected");
|
126
|
+
hide_snapshot_controls();
|
127
|
+
clear_upload_data();
|
128
|
+
camera.show_stream();
|
129
|
+
};
|
130
|
+
|
131
|
+
var hide_snapshot_controls = function() {
|
132
|
+
$("#discard_snapshot, #api_url").hide();
|
133
|
+
$("#upload_result, #upload_status").html("");
|
134
|
+
$("#show_stream").hide();
|
135
|
+
};
|
136
|
+
|
137
|
+
$("#take_snapshots").click(function() { take_snapshots(1); });
|
138
|
+
$("#snapshots").on("click", ".item", select_snapshot);
|
139
|
+
$("#upload_snapshot").click(upload_snapshot);
|
140
|
+
$("#discard_snapshot").click(discard_snapshot);
|
141
|
+
$("#show_stream").click(show_stream);
|
142
|
+
|
143
|
+
var options = {
|
144
|
+
shutter_ogg_url: "/assets/faceauth/shutter.ogg",
|
145
|
+
shutter_mp3_url: "/assets/faceauth/shutter.mp3",
|
146
|
+
swf_url: "/assets/faceauth/jpeg_camera.swf"
|
147
|
+
}
|
148
|
+
|
149
|
+
camera = new JpegCamera("#camera", options).ready(function(info) {
|
150
|
+
$("#take_snapshots").show();
|
151
|
+
$("#camera_info").html("Camera resolution: " + info.video_width + "x" + info.video_height);
|
152
|
+
this.get_stats(update_stream_stats);
|
153
|
+
$("#discard_snapshot").hide();
|
154
|
+
});
|
155
|
+
|
156
|
+
var hide_alerts = function() {
|
157
|
+
$(".alert").fadeTo(2000, 500).slideUp(500, function() {
|
158
|
+
$(".alert").slideUp(500);
|
159
|
+
});
|
160
|
+
$("#upload_result").removeClass("alert alert-danger");
|
161
|
+
}
|
162
|
+
|
163
|
+
var generate_validation_messages = function(message) {
|
164
|
+
$("#upload_result").html(message);
|
165
|
+
$("#upload_snapshot").prop("disabled", false);
|
166
|
+
$("#upload_result").addClass("alert alert-danger");
|
167
|
+
hide_alerts();
|
168
|
+
}
|
169
|
+
|
170
|
+
var isEmail = function(email) {
|
171
|
+
var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
|
172
|
+
return regex.test(email);
|
173
|
+
}
|
174
|
+
|
175
|
+
|
176
|
+
}
|
177
|
+
});
|
Binary file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
/*! SWFObject + Canvas-to-Blob + JpegCamera */
|
2
|
+
|
3
|
+
/* SWFObject v2.2 <http://code.google.com/p/swfobject/>
|
4
|
+
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
|
5
|
+
*/
|
6
|
+
var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
|
7
|
+
!function(t){"use strict";var e=t.HTMLCanvasElement&&t.HTMLCanvasElement.prototype,o=t.Blob&&function(){try{return Boolean(new Blob)}catch(t){return!1}}(),n=o&&t.Uint8Array&&function(){try{return 100===new Blob([new Uint8Array(100)]).size}catch(t){return!1}}(),r=t.BlobBuilder||t.WebKitBlobBuilder||t.MozBlobBuilder||t.MSBlobBuilder,a=/^data:((.*?)(;charset=.*?)?)(;base64)?,/,i=(o||r)&&t.atob&&t.ArrayBuffer&&t.Uint8Array&&function(t){var e,i,l,u,b,c,d,B,f;if(e=t.match(a),!e)throw new Error("invalid data URI");for(i=e[2]?e[1]:"text/plain"+(e[3]||";charset=US-ASCII"),l=!!e[4],u=t.slice(e[0].length),b=l?atob(u):decodeURIComponent(u),c=new ArrayBuffer(b.length),d=new Uint8Array(c),B=0;B<b.length;B+=1)d[B]=b.charCodeAt(B);return o?new Blob([n?d:c],{type:i}):(f=new r,f.append(c),f.getBlob(i))};t.HTMLCanvasElement&&!e.toBlob&&(e.mozGetAsFile?e.toBlob=function(t,o,n){t(n&&e.toDataURL&&i?i(this.toDataURL(o,n)):this.mozGetAsFile("blob",o))}:e.toDataURL&&i&&(e.toBlob=function(t,e,o){t(i(this.toDataURL(e,o)))})),"function"==typeof define&&define.amd?define(function(){return i}):"object"==typeof module&&module.exports?module.exports=i:t.dataURLtoBlob=i}(window);
|
8
|
+
|
9
|
+
/*! JpegCamera 1.3.3 | 2016-09-18
|
10
|
+
(c) 2013 Adam Wrobel
|
11
|
+
https://amw.github.io/jpeg_camera */
|
12
|
+
(function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o={}.hasOwnProperty,p=function(a,b){function c(){this.constructor=a}for(var d in b)o.call(b,d)&&(a[d]=b[d]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a};if(a=function(){function a(a,b){if("string"==typeof a&&(a=document.getElementById(a.replace("#",""))),!a||!a.offsetWidth)throw"JpegCamera: invalid container";a.innerHTML="",this.view_width=parseInt(a.offsetWidth,10),this.view_height=parseInt(a.offsetHeight,10),this.container=document.createElement("div"),this.container.style.width="100%",this.container.style.height="100%",this.container.style.position="relative",a.appendChild(this.container),this.options=this._extend({},this.constructor.DefaultOptions,b),this._engine_init()}return a.DefaultOptions={shutter_ogg_url:"/jpeg_camera/shutter.ogg",shutter_mp3_url:"/jpeg_camera/shutter.mp3",swf_url:"/jpeg_camera/jpeg_camera.swf",on_debug:function(a){return console&&console.log?console.log("JpegCamera: "+a):void 0},quality:.9,shutter:!0,mirror:!1,timeout:0,retry_success:!1,scale:1},a._canvas_supported=!!document.createElement("canvas").getContext,a.canvas_supported=function(){return this._canvas_supported},a.prototype.ready=function(a){return this.options.on_ready=a,this.options.on_ready&&this._is_ready&&this.options.on_ready.call(this,{video_width:this.video_width,video_height:this.video_height}),this},a.prototype._is_ready=!1,a.prototype.error=function(a){return this.options.on_error=a,this.options.on_error&&this._error_occured&&this.options.on_error.call(this,this._error_occured),this},a.prototype._error_occured=!1,a.StatsCaptureScale=.2,a.prototype.get_stats=function(b){var c,e;return c=new d(this,{}),this._engine_capture(c,!1,.1,a.StatsCaptureScale),e=this,c.get_stats(function(a){return b.call(e,a)})},a.prototype.capture=function(a){var b,c,e;return null==a&&(a={}),c=new d(this,a),this._snapshots[c.id]=c,e=c._options(),e.shutter&&this._engine_play_shutter_sound(),b=Math.min(1,e.scale),b=Math.max(.01,b),this._engine_capture(c,e.mirror,e.quality,b),c},a.prototype._snapshots={},a.prototype.show_stream=function(){return this._engine_show_stream(),this._displayed_snapshot=null,this},a.prototype.discard_all=function(){var a,b,c;this._displayed_snapshot&&this.show_stream(),c=this._snapshots;for(a in c)b=c[a],this._engine_discard(b),b._discarded=!0;return this._snapshots={},this},a.prototype._extend=function(a){var b,c,d,e,f,g;for(d=Array.prototype.slice.call(arguments,1),f=0,g=d.length;g>f;f++)if(c=d[f])for(b in c)e=c[b],a[b]=e;return a},a.prototype._debug=function(a){return this.options.on_debug?this.options.on_debug.call(this,a):void 0},a.prototype._display=function(a){return this._engine_display(a),this._displayed_snapshot=a},a.prototype._displayed_snapshot=null,a.prototype._discard=function(a){return this._displayed_snapshot===a&&this.show_stream(),this._engine_discard(a),a._discarded=!0,delete this._snapshots[a.id]},a.prototype._prepared=function(a,b){var c;return this.video_width=a,this.video_height=b,this._debug("Camera resolution "+this.video_width+"x"+this.video_height+"px"),c=this,setTimeout(function(){return c._wait_until_stream_looks_ok(!0)},1)},a.prototype._wait_until_stream_looks_ok=function(a){return this.get_stats(function(b){var c;return b.std>2?(this._debug("Stream mean gray value = "+b.mean+" standard deviation = "+b.std),this._debug("Camera is ready"),this._is_ready=!0,this.options.on_ready?this.options.on_ready.call(this,{video_width:this.video_width,video_height:this.video_height}):void 0):(a&&this._debug("Stream mean gray value = "+b.mean+" standard deviation = "+b.std),c=this,setTimeout(function(){return c._wait_until_stream_looks_ok(!1)},100))})},a.prototype._got_error=function(a){return this._debug("Error - "+a),this._error_occured=a,this.options.on_error?this.options.on_error.call(this,this._error_occured):void 0},a.prototype._block_element_access=function(){return this._overlay=document.createElement("div"),this._overlay.style.width="100%",this._overlay.style.height="100%",this._overlay.style.position="absolute",this._overlay.style.top=0,this._overlay.style.left=0,this._overlay.style.zIndex=2,this.container.appendChild(this._overlay)},a.prototype._overlay=null,a.prototype.view_width=null,a.prototype.view_height=null,a._add_prefixed_style=function(a,b,c){var d;return d=b.charAt(0).toUpperCase()+b.slice(1),a.style[b]=c,a.style["Webkit"+d]=c,a.style["Moz"+d]=c,a.style["ms"+d]=c,a.style["O"+d]=c},a}(),navigator.getUserMedia||(navigator.getUserMedia=navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia),window.AudioContext||(window.AudioContext=window.webkitAudioContext),h=function(){var a;if(a=document.createElement("canvas"),a.getContext&&!a.toBlob)throw"JpegCamera: Canvas-to-Blob is not loaded"},navigator.getUserMedia&&(h(),l="audio/ogg; codecs=vorbis",i="audio/mpeg; ",f=function(a){var b;return b=document.createElement("video"),!(!b.canPlayType||!b.canPlayType(a).replace(/no/,""))},c=function(b){function c(){return m=c.__super__.constructor.apply(this,arguments)}return p(c,b),c.prototype._engine_init=function(){var b,c,d,e,g,h,j;this._debug("Using HTML5 engine"),j=Math.floor(.2*this.view_height),e=Math.floor(.2*this.view_width),this.message=document.createElement("div"),this.message["class"]="message",this.message.style.width="100%",this.message.style.height="100%",a._add_prefixed_style(this.message,"boxSizing","border-box"),this.message.style.overflow="hidden",this.message.style.textAlign="center",this.message.style.paddingTop=""+j+"px",this.message.style.paddingBottom=""+j+"px",this.message.style.paddingLeft=""+e+"px",this.message.style.paddingRight=""+e+"px",this.message.style.position="absolute",this.message.style.zIndex=3,this.message.innerHTML="Please allow camera access when prompted by the browser.<br><br>Look for camera icon around your address bar.",this.container.appendChild(this.message),this.video_container=document.createElement("div"),this.video_container.style.width=""+this.view_width+"px",this.video_container.style.height=""+this.view_height+"px",this.video_container.style.overflow="hidden",this.video_container.style.position="absolute",this.video_container.style.zIndex=1,this.container.appendChild(this.video_container),this.video=document.createElement("video"),this.video.autoplay=!0,a._add_prefixed_style(this.video,"transform","scalex(-1.0)"),window.AudioContext&&(f(l)?this._load_shutter_sound(this.options.shutter_ogg_url):f(i)&&this._load_shutter_sound(this.options.shutter_mp3_url)),d={video:{optional:[{minWidth:1280},{minWidth:640},{minWidth:480},{minWidth:360}]}},h=this,g=function(a){return h._remove_message(),window.URL?h.video.src=URL.createObjectURL(a):h.video.src=a,h._block_element_access(),h._wait_for_video_ready()},c=function(a){var b,c,d;h.message.innerHTML='<span style="color: red;">You have denied camera access.</span><br><br>Look for camera icon around your address bar to change your decision.',b=a.code;for(c in a)if(d=a[c],"code"!==c)return void h._got_error(c);return h._got_error("UNKNOWN ERROR")};try{return navigator.getUserMedia(d,g,c)}catch(k){return b=k,navigator.getUserMedia("video",g,c)}},c.prototype._engine_play_shutter_sound=function(){var a;if(this.shutter_buffer)return a=this.audio_context.createBufferSource(),a.buffer=this.shutter_buffer,a.connect(this.audio_context.destination),a.start(0)},c.prototype._engine_capture=function(a,b,c,d){var e,f,g;return g=this._get_capture_crop(),e=document.createElement("canvas"),e.width=Math.round(g.width*d),e.height=Math.round(g.height*d),f=e.getContext("2d"),f.drawImage(this.video,g.x_offset,g.y_offset,g.width,g.height,0,0,Math.round(g.width*d),Math.round(g.height*d)),a._canvas=e,a._mirror=b,a._quality=c},c.prototype._engine_display=function(b){return this.displayed_canvas&&this.container.removeChild(this.displayed_canvas),this.displayed_canvas=b._canvas,this.displayed_canvas.style.width=""+this.view_width+"px",this.displayed_canvas.style.height=""+this.view_height+"px",this.displayed_canvas.style.top=0,this.displayed_canvas.style.left=0,this.displayed_canvas.style.position="absolute",this.displayed_canvas.style.zIndex=2,a._add_prefixed_style(this.displayed_canvas,"transform","scalex(-1.0)"),this.container.appendChild(this.displayed_canvas)},c.prototype._engine_get_canvas=function(a){var b,c;return b=document.createElement("canvas"),b.width=a._canvas.width,b.height=a._canvas.height,c=b.getContext("2d"),c.drawImage(a._canvas,0,0),b},c.prototype._engine_get_image_data=function(a){var b,c;return b=a._canvas,c=b.getContext("2d"),c.getImageData(0,0,b.width,b.height)},c.prototype._engine_get_blob=function(a,b,c,d,e){var f,g;return c?(f=document.createElement("canvas"),f.width=a._canvas.width,f.height=a._canvas.height,g=f.getContext("2d"),g.setTransform(1,0,0,1,0,0),g.translate(f.width,0),g.scale(-1,1),g.drawImage(a._canvas,0,0)):f=a._canvas,f.toBlob(function(a){return e(a)},b,d)},c.prototype._engine_discard=function(a){return a._xhr&&a._xhr.abort(),delete a._xhr,delete a._canvas},c.prototype._engine_show_stream=function(){return this.displayed_canvas&&(this.container.removeChild(this.displayed_canvas),this.displayed_canvas=null),this.video_container.style.display="block"},c.prototype._engine_upload=function(a,b,c,d){return this._debug("Uploading the file"),a.get_blob(function(e){var f,g;return f=function(b){return delete a._xhr,a._status=b.target.status,a._response=b.target.responseText,a._status>=200&&a._status<300?a._upload_done():(a._error_message=b.target.statusText||"Unknown error",a._upload_fail())},g=new XMLHttpRequest,g.open("POST",b),g.timeout=d,c&&g.setRequestHeader("X-CSRF-Token",c),g.onload=f,g.onerror=f,g.onabort=f,g.send(e),a._xhr=g},"image/jpeg")},c.prototype._remove_message=function(){return this.message.style.display="none"},c.prototype._load_shutter_sound=function(a){var b,c;if(!this.audio_context)return this.audio_context=new AudioContext,b=new XMLHttpRequest,b.open("GET",a,!0),b.responseType="arraybuffer",c=this,b.onload=function(){return c.audio_context.decodeAudioData(b.response,function(a){return c.shutter_buffer=a})},b.send()},c.prototype._wait_for_video_ready=function(){var a,b,c,d;return d=parseInt(this.video.videoWidth),c=parseInt(this.video.videoHeight),d>0&&c>0?(this.video_container.appendChild(this.video),this.video_width=d,this.video_height=c,a=this._get_video_crop(),this.video.style.position="relative",this.video.style.width=""+a.width+"px",this.video.style.height=""+a.height+"px",this.video.style.left=""+a.x_offset+"px",this.video.style.top=""+a.y_offset+"px",this._prepared(this.video_width,this.video_height)):this._status_checks_count>100?this._got_error("Camera failed to initialize in 10 seconds"):(this._status_checks_count++,b=this,setTimeout(function(){return b._wait_for_video_ready()},100))},c.prototype._status_checks_count=0,c.prototype._get_video_crop=function(){var a,b,c,d,e;return c=this.video_width/this.video_height,e=this.view_width/this.view_height,c>=e?(this._debug("Filling height"),d=this.view_height/this.video_height,b=Math.round(this.video_width*d),{width:b,height:this.view_height,x_offset:-Math.floor((b-this.view_width)/2),y_offset:0}):(this._debug("Filling width"),d=this.view_width/this.video_width,a=Math.round(this.video_height*d),{width:this.view_width,height:a,x_offset:0,y_offset:-Math.floor((a-this.view_height)/2)})},c.prototype._get_capture_crop=function(){var a,b,c,d;return c=this.video_width/this.video_height,d=this.view_width/this.view_height,c>=d?(b=Math.round(this.video_height*d),{width:b,height:this.video_height,x_offset:Math.floor((this.video_width-b)/2),y_offset:0}):(a=Math.round(this.video_width/d),{width:this.video_width,height:a,x_offset:0,y_offset:Math.floor((this.video_height-a)/2)})},c}(a),window.JpegCamera=c),!window.swfobject)throw"JpegCamera: SWFObject is not loaded";k="9",j=!window.JpegCamera||!window.AudioContext||window.jpeg_camera_force_flash,g=function(){return window.swfobject&&swfobject.hasFlashPlayerVersion(k)},j&&g()&&(b=function(b){function c(){return n=c.__super__.constructor.apply(this,arguments)}return p(c,b),c._send_message=function(a,b){var c,d;return(d=this._instances[parseInt(a)])?(c=Array.prototype.slice.call(arguments,2),this.prototype[b].apply(d,c)):void 0},c._instances={},c._next_id=1,c.prototype._engine_init=function(){var a,b,c,d,e,f,g;return this._debug("Using Flash engine"),this._id=this.constructor._next_id++,this.constructor._instances[this._id]=this,this.view_width<215||this.view_height<138?void this._got_error("camera is too small to display privacy dialog"):(d="flash_object_"+this._id,f={loop:"false",allowScriptAccess:"always",allowFullScreen:"false",quality:"best",wmode:"opaque",menu:"false"},a={id:d,align:"middle"},e={id:this._id,width:this.view_width,height:this.view_height,shutter_url:this.options.shutter_mp3_url},g=this,b=function(a){return a.success?(g._debug("Flash loaded"),g._flash=document.getElementById(d)):g._got_error("Flash loading failed.")},c=document.createElement("div"),c.id="jpeg_camera_flash_"+this._id,c.style.width="100%",c.style.height="100%",this.container.appendChild(c),swfobject.embedSWF(this.options.swf_url,c.id,this.view_width,this.view_height,"9",null,e,f,a,b))},c.prototype._engine_play_shutter_sound=function(){return this._flash._play_shutter()},c.prototype._engine_capture=function(a,b,c,d){return this._flash._capture(a.id,b,c,d)},c.prototype._engine_display=function(a){return this._flash._display(a.id)},c.prototype._engine_get_canvas=function(a){var b,c;return a._image_data||(a._image_data=this._engine_get_image_data(a)),b=document.createElement("canvas"),b.width=a._image_data.width,b.height=a._image_data.height,c=b.getContext("2d"),c.putImageData(a._image_data,0,0),b},c.prototype._engine_get_image_data=function(b){var c,d,e,f,g,h,i,j,k,l,m,n,o;for(f=this._flash._get_image_data(b.id),a.canvas_supported()?(d=document.createElement("canvas"),d.width=f.width,d.height=f.height,e=d.getContext("2d"),l=e.createImageData(f.width,f.height)):l={data:[],width:f.width,height:f.height},o=f.data,h=m=0,n=o.length;n>m;h=++m)j=o[h],i=4*h,k=j>>16&255,g=j>>8&255,c=255&j,l.data[i+0]=k,l.data[i+1]=g,l.data[i+2]=c,l.data[i+3]=255;return l},c.prototype._engine_get_blob=function(a,b,c,d,e){var f,g;return a._extra_canvas||(a._extra_canvas=this._engine_get_canvas(a)),c?(f=document.createElement("canvas"),f.width=a._canvas.width,f.height=a._canvas.height,g=f.getContext("2d"),g.setTransform(1,0,0,1,0,0),g.translate(f.width,0),g.scale(-1,1),g.drawImage(a._extra_canvas,0,0)):f=a._extra_canvas,f.toBlob(function(a){return e(a)},b,d)},c.prototype._engine_discard=function(a){return this._flash._discard(a.id)},c.prototype._engine_show_stream=function(){return this._flash._show_stream()},c.prototype._engine_upload=function(a,b,c,d){return this._flash._upload(a.id,b,c,d)},c.prototype._flash_prepared=function(a,b){return this._block_element_access(),document.body.tabIndex=0,document.body.focus(),this._prepared(a,b)},c.prototype._flash_upload_complete=function(a,b,c,d){var e;return a=parseInt(a),e=this._snapshots[a],e._status=parseInt(b),e._response=d,e._status>=200&&e._status<300?e._upload_done():(e._error_message=c,e._upload_fail())},c}(a),window.JpegCamera=b),d=function(){function b(a,b){this.camera=a,this.options=b,this.id=this.constructor._next_snapshot_id++}return b._next_snapshot_id=1,b.prototype._discarded=!1,b.prototype.show=function(){return this._discarded&&raise("discarded snapshot cannot be used"),this.camera._display(this),this},b.prototype.hide=function(){return this.camera.displayed_snapshot()===this&&this.camera.show_stream(),this},b.prototype.get_stats=function(a){return this._discarded&&raise("discarded snapshot cannot be used"),this.get_image_data(function(b){return this._get_stats(b,a)})},b.prototype.get_canvas=function(b){var c;return this._discarded&&raise("discarded snapshot cannot be used"),!a._canvas_supported,c=this,setTimeout(function(){return c._extra_canvas||(c._extra_canvas=c.camera._engine_get_canvas(c)),a._add_prefixed_style(c._extra_canvas,"transform","scalex(-1.0)"),b.call(c,c._extra_canvas)},1),!0},b.prototype._extra_canvas=null,b.prototype.get_blob=function(b,c){var d;return null==c&&(c="image/jpeg"),this._discarded&&raise("discarded snapshot cannot be used"),!a._canvas_supported,d=this,setTimeout(function(){var a,e;return d._blob_mime!==c&&(d._blob=null),d._blob_mime=c,d._blob?b.call(d,d._blob):(a=d.options.mirror,e=d.options.quality,d.camera._engine_get_blob(d,c,a,e,function(a){return d._blob=a,b.call(d,d._blob)}))},1),!0},b.prototype._blob=null,b.prototype._blob_mime=null,b.prototype.get_image_data=function(a){var b;return this._discarded&&raise("discarded snapshot cannot be used"),b=this,setTimeout(function(){return b._image_data||(b._image_data=b.camera._engine_get_image_data(b)),a.call(b,b._image_data)},1),null},b.prototype._image_data=null,b.prototype.upload=function(a){var b;if(null==a&&(a={}),this._discarded&&raise("discarded snapshot cannot be used"),this._uploading)return void this.camera._debug("Upload already in progress");if(this._uploading=!0,this._retry=1,this._upload_options=a,b=this._options(),!b.api_url)throw this.camera._debug("Snapshot#upload called without valid api_url"),"Snapshot#upload called without valid api_url";return this._start_upload(b),this},b.prototype._upload_options={},b.prototype._uploading=!1,b.prototype._retry=1,b.prototype.done=function(a){var b;return this._discarded&&raise("discarded snapshot cannot be used"),this._upload_options.on_upload_done=a,b=this._options(),b.on_upload_done&&this._done&&b.on_upload_done.call(this,this._response),this},b.prototype._done=!1,b.prototype._response=null,b.prototype.fail=function(a){var b;return this._discarded&&raise("discarded snapshot cannot be used"),this._upload_options.on_upload_fail=a,b=this._options(),b.on_upload_fail&&this._fail&&b.on_upload_fail.call(this,this._status,this._error_message,this._response),this},b.prototype._fail=!1,b.prototype._status=null,b.prototype._error_message=null,b.prototype.discard=function(){return this.camera._discard(this),delete this._extra_canvas,delete this._image_data,void delete this._blob},b.prototype._options=function(){return this.camera._extend({},this.camera.options,this.options,this._upload_options)},b.prototype._start_upload=function(a){var b;return b="string"==typeof a.csrf_token&&a.csrf_token.length>0?a.csrf_token:null,this._done=!1,this._response=null,this._fail=!1,this._status=null,this._error_message=null,this.camera._engine_upload(this,a.api_url,b,a.timeout)},b.prototype._get_stats=function(a,b){var c,d,f,g,h,i,j,k,l,m,n;if(!this._stats){for(i=a.width*a.height,j=0,d=new Array(i),f=l=0;i>l;f=l+=1)g=4*f,c=.2126*a.data[g+0]+.7152*a.data[g+1]+.0722*a.data[g+2],c=Math.round(c),j+=c,d[f]=c;for(h=Math.round(j/i),k=0,m=0,n=d.length;n>m;m++)c=d[m],k+=Math.pow(c-h,2);this._stats=new e,this._stats.mean=h,this._stats.std=Math.round(Math.sqrt(k/i))}return b.call(this,this._stats)},b.prototype._stats=null,b.prototype._upload_done=function(){var a,b,c,d;return this.camera._debug("Upload completed with status "+this._status),this._done=!0,a=this._options(),c=a.retry_success&&a.retry_if&&a.retry_if.call(this,this._status,this._error_message,this._response,this._retry),!0===c&&(c=0),"number"==typeof c?(this._retry++,c>0?(b=parseInt(c),this.camera._debug("Will retry the upload in "+b+"ms (attempt #"+this._retry+")"),d=this,setTimeout(function(){return d._start_upload(a)},b)):(this.camera._debug("Will retry the upload immediately (attempt #"+this._retry+")"),this._start_upload(a))):(this._uploading=!1,a.on_upload_done?a.on_upload_done.call(this,this._response):void 0)},b.prototype._upload_fail=function(){var a,b,c,d;return this.camera._debug("Upload failed with status "+this._status),this._fail=!0,a=this._options(),c=a.retry_if&&a.retry_if.call(this,this._status,this._error_message,this._response,this._retry),!0===c&&(c=0),"number"==typeof c?(this._retry++,c>0?(b=parseInt(c),this.camera._debug("Will retry the upload in "+b+"ms (attempt #"+this._retry+")"),d=this,setTimeout(function(){return d._start_upload(a)},b)):(this.camera._debug("Will retry the upload immediately (attempt #"+this._retry+")"),this._start_upload(a))):(this._uploading=!1,a.on_upload_fail?a.on_upload_fail.call(this,this._status,this._error_message,this._response):void 0)},b}(),e=function(){function a(){}return a.prototype.mean=null,a.prototype.std=null,a}()}).call(this);
|
Binary file
|
Binary file
|
@@ -0,0 +1,17 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_self
|
14
|
+
*= require_tree .
|
15
|
+
|
16
|
+
*/
|
17
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
/*
|
2
|
+
Place all the styles related to the matching controller here.
|
3
|
+
They will automatically be included in application.css.
|
4
|
+
*/
|
5
|
+
|
6
|
+
input {}
|
7
|
+
|
8
|
+
.faceauth-signin-form {
|
9
|
+
margin: 1em;
|
10
|
+
}
|
11
|
+
|
12
|
+
#user_email {
|
13
|
+
width: 250px;
|
14
|
+
}
|
15
|
+
|
16
|
+
#camera {
|
17
|
+
display: inline-block;
|
18
|
+
background-color: #eee;
|
19
|
+
width: 600px;
|
20
|
+
height: 400px;
|
21
|
+
margin: 1em 0em 1em 0em;
|
22
|
+
}
|
23
|
+
|
24
|
+
#camera .placeholder {
|
25
|
+
padding: 0.5em;
|
26
|
+
}
|
27
|
+
|
28
|
+
#snapshots {
|
29
|
+
height: 150px;
|
30
|
+
margin: 0.5em 0;
|
31
|
+
padding: 3px;
|
32
|
+
border: 1px solid white;
|
33
|
+
white-space: nowrap;
|
34
|
+
overflow-x: auto;
|
35
|
+
overflow-y: hidden;
|
36
|
+
}
|
37
|
+
|
38
|
+
#snapshots canvas,
|
39
|
+
#snapshots img {
|
40
|
+
box-sizing: border-box;
|
41
|
+
-webkit-box-sizing: border-box;
|
42
|
+
-moz-box-sizing: border-box;
|
43
|
+
-ms-box-sizing: border-box;
|
44
|
+
-o-box-sizing: border-box;
|
45
|
+
height: 100%;
|
46
|
+
margin-left: 3px;
|
47
|
+
border: 3px solid transparent;
|
48
|
+
}
|
49
|
+
|
50
|
+
#snapshots .selected {
|
51
|
+
border: 3px solid #000;
|
52
|
+
}
|
53
|
+
|
54
|
+
button,
|
55
|
+
#upload_status,
|
56
|
+
#upload_result,
|
57
|
+
#loader {
|
58
|
+
margin: 0.5em;
|
59
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_dependency "faceauth/application_controller"
|
2
|
+
|
3
|
+
module Faceauth
|
4
|
+
class FacesController < ApplicationController
|
5
|
+
skip_before_filter :verify_authenticity_token
|
6
|
+
|
7
|
+
|
8
|
+
#Reading configuration settings to identify model being used in devise configuration to manage users base.
|
9
|
+
MODEL = Faceauth.model_name.camelize.constantize
|
10
|
+
|
11
|
+
#Initializing user object
|
12
|
+
def new
|
13
|
+
@user = MODEL.new
|
14
|
+
end
|
15
|
+
|
16
|
+
# This method handles the picture recieved from Webcam.
|
17
|
+
# Saves the picture and invoke facial recongition system by comparing two pictures by identifying the user.
|
18
|
+
def create
|
19
|
+
@user = MODEL.find_by(Faceauth.email_column.to_sym => params[:email])
|
20
|
+
data = request.raw_post
|
21
|
+
tmp_file = "#{Rails.root}/tmp/#{@user.email}_auth_source.png"
|
22
|
+
File.open(tmp_file, 'wb') do |f|
|
23
|
+
f.write(data)
|
24
|
+
end
|
25
|
+
image = MiniMagick::Image.open(tmp_file)
|
26
|
+
if @user.present? && !@user.send("#{Faceauth.signup_picture_column.to_sym}").blank?
|
27
|
+
@user.send("#{Faceauth.signin_picture_column.to_sym}=", image)
|
28
|
+
@user.save
|
29
|
+
File.delete(tmp_file) if File.exist?(tmp_file)
|
30
|
+
request_uri = "#{request.protocol}#{request.host}"
|
31
|
+
response = Faceauth::Authenticate.login(@user, request_uri)
|
32
|
+
if !response.blank? && response["verified"]
|
33
|
+
@user.reload
|
34
|
+
sign_in(@user)
|
35
|
+
render json: {message: "Authentication Successful", status: "success", location: main_app.try(Faceauth.redirect_url)}
|
36
|
+
else
|
37
|
+
render json: {message: "Verification Failed. Please try again!", status: "failed"}
|
38
|
+
end
|
39
|
+
else
|
40
|
+
render json: {message: "Sorry! System didn't recognize you'", status: "failed"}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<div class="faceauth-signin-form">
|
2
|
+
<h2>Log in using your face</h2>
|
3
|
+
<strong>Note:</strong> Please ensure to have proper lighting in your environment before taking a snap.
|
4
|
+
<div id="upload_status"></div>
|
5
|
+
<div id="upload_result"></div>
|
6
|
+
* Email: <input type="email" name="email" id="user_email"><br>
|
7
|
+
<div id="camera">
|
8
|
+
<div class="placeholder">
|
9
|
+
Your browser does not support camera access.<br>
|
10
|
+
We recommend
|
11
|
+
<a href="https://www.google.com/chrome/" target="_blank">Chrome</a>
|
12
|
+
— modern, secure, fast browser from Google.<br>
|
13
|
+
It's free.
|
14
|
+
</div>
|
15
|
+
</div><br>
|
16
|
+
<button id="take_snapshots" class="btn btn-success">Take a snap</button>
|
17
|
+
<button id="discard_snapshot" class="btn btn-danger">Discard snapshot</button>
|
18
|
+
<button id="upload_snapshot" class="btn btn-default">Sign In</button>
|
19
|
+
<%=image_tag("/assets/faceauth/loader.gif", id: "upload_loader", style: "display:none;")%>
|
20
|
+
<br>
|
21
|
+
<div id="snapshots"></div>
|
22
|
+
</div>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Faceauth</title>
|
5
|
+
<%= stylesheet_link_tag "faceauth/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "faceauth/application" %>
|
7
|
+
<!-- Latest compiled and minified CSS -->
|
8
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
9
|
+
|
10
|
+
<!-- Optional theme -->
|
11
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
|
12
|
+
|
13
|
+
<!-- Latest compiled and minified JavaScript -->
|
14
|
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
15
|
+
<%= csrf_meta_tags %>
|
16
|
+
</head>
|
17
|
+
|
18
|
+
<body>
|
19
|
+
|
20
|
+
<%= yield %>
|
21
|
+
|
22
|
+
</body>
|
23
|
+
</html>
|
data/config/routes.rb
ADDED
data/lib/faceauth.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "faceauth/engine"
|
2
|
+
require "faceauth/configuration"
|
3
|
+
require "faceauth/authenticate"
|
4
|
+
require "faceauth/gem_dependencies"
|
5
|
+
|
6
|
+
module Faceauth
|
7
|
+
extend Configuration
|
8
|
+
# setting up default configuration
|
9
|
+
define_setting :model_name, "user"
|
10
|
+
define_setting :uploader_name, "carrierwave"
|
11
|
+
define_setting :redirect_url, "root_path"
|
12
|
+
define_setting :findface_api_key, "YOUR_API_KEY"
|
13
|
+
define_setting :signup_picture_column, "user_picture"
|
14
|
+
define_setting :signin_picture_column, "last_sign_in_picture"
|
15
|
+
define_setting :email_column, "email"
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Faceauth
|
2
|
+
class Authenticate
|
3
|
+
class << self
|
4
|
+
|
5
|
+
#This method is responsibile for running comparisons between user picture & picture submitted during sign in process.
|
6
|
+
#It servers the binary verification result.
|
7
|
+
def login(user, request_uri)
|
8
|
+
Findface.api_key = Faceauth.findface_api_key
|
9
|
+
begin
|
10
|
+
options = {
|
11
|
+
"photo1": request_uri + "#{user.send(Faceauth.signup_picture_column).url}",
|
12
|
+
"photo2": request_uri + "#{user.send(Faceauth.signin_picture_column).url}",
|
13
|
+
"threshold": "strict"
|
14
|
+
}
|
15
|
+
return Findface::Utility.verify options
|
16
|
+
rescue Findface::Error => e
|
17
|
+
puts e.parsed_response
|
18
|
+
puts "\n"
|
19
|
+
puts e.message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Configuration
|
2
|
+
|
3
|
+
#Initializing the configuration module
|
4
|
+
def configuration
|
5
|
+
yield self
|
6
|
+
end
|
7
|
+
|
8
|
+
#Method to define configuration variables & assign default values or values provied.
|
9
|
+
def define_setting(name, default = nil)
|
10
|
+
class_variable_set("@@#{name}", default)
|
11
|
+
define_class_method "#{name}=" do |value|
|
12
|
+
class_variable_set("@@#{name}", value)
|
13
|
+
end
|
14
|
+
define_class_method name do
|
15
|
+
class_variable_get("@@#{name}")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
#Method provides valid definition for class type of variables.
|
22
|
+
def define_class_method(name, &block)
|
23
|
+
(class << self; self; end).instance_eval do
|
24
|
+
define_method name, &block
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Faceauth
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace Faceauth
|
4
|
+
|
5
|
+
initializer "faceauth.assets.precompile" do |app|
|
6
|
+
#The supporting jpeg_camera assets that are responsible for camera functionalities in plugin provided for compilation.
|
7
|
+
app.config.assets.precompile += %w(jpeg_camera.min.js canvas-to-blob.min.js swfobject.js shutter.mp3 shutter.ogg jpeg_camera.swf)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Faceauth
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base #:nodoc:
|
4
|
+
source_root File.expand_path("../../templates", __FILE__)
|
5
|
+
desc "Creates a Conschedule initializer and copy migration file to your application."
|
6
|
+
|
7
|
+
#Method to generate a initializer configuration file with default configuration variables available.
|
8
|
+
def copy_initializer
|
9
|
+
copy_file "initializer.rb", "config/initializers/faceauth.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
#Method to read and display README document contents in terminal when generator is invoked.
|
13
|
+
def show_readme
|
14
|
+
readme "README"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Faceauth
|
2
|
+
module Generators
|
3
|
+
module ViewPathTemplates #:nodoc:
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
#Settings to enable aliase options.
|
7
|
+
included do
|
8
|
+
class_option :views, aliases: "-v", type: :array, desc: "Select specific views to generate (form)"
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
#Method that looks up for plugin views & exclude certain views based up on the alias option provided.
|
13
|
+
def view_directory(name)
|
14
|
+
directory "faceauth/faces", "app/views/faceauth/faces", exclude_pattern: /index|_faces/ if name.to_s == "form"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ViewsGenerator < Rails::Generators::Base #:nodoc:
|
19
|
+
#This generator module is responsbile for accessing plugin templates and copying them to parent rails application.
|
20
|
+
include ViewPathTemplates
|
21
|
+
source_root File.expand_path("../../../../app/views", __FILE__)
|
22
|
+
desc "Copies Faceauth views to your application."
|
23
|
+
def copy_views
|
24
|
+
if options[:views]
|
25
|
+
options[:views].each do |directory|
|
26
|
+
view_directory directory.to_sym
|
27
|
+
end
|
28
|
+
else
|
29
|
+
view_directory :form
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
===============================================================================
|
2
|
+
|
3
|
+
Some setup you must do manually if you haven't yet:
|
4
|
+
|
5
|
+
1. Create a migration and add two column names: "user_picture" & "last_sign_in_picture" to your devise user model which you are using to store user login information
|
6
|
+
|
7
|
+
Run the migrations: rails db:migrate
|
8
|
+
|
9
|
+
2. Add the following to your routes.rb file:
|
10
|
+
|
11
|
+
mount Faceauth::Engine, at: "/"
|
12
|
+
|
13
|
+
(NOTE: You may mount it at any path, not just "/")
|
14
|
+
|
15
|
+
3. Ensure you have set "findface_api_key" and "redirect_url" in config/initializers/faceauth.rb
|
16
|
+
|
17
|
+
# config.findface_api_key = "YOUR_API_KEY"
|
18
|
+
# config.redirect_url = "root_path" #By default, the option takes root_path of your rails application.
|
19
|
+
|
20
|
+
5. If you wish to pass your custom model name & column names, please set the names using
|
21
|
+
|
22
|
+
# config.model_name = "your_model_name" (Default is 'User')
|
23
|
+
# config.signup_picture_column = "custom_column_name" (Default is 'user_picture')
|
24
|
+
# config.signin_picture_column = "custom_column_name" (Default is 'last_sign_in_picture')
|
25
|
+
|
26
|
+
6. You can copy Faceauth views (for customization) to your app by running:
|
27
|
+
|
28
|
+
rails g faceauth:views
|
29
|
+
|
30
|
+
OR
|
31
|
+
|
32
|
+
rails g faceauth:views -v form
|
33
|
+
|
34
|
+
===============================================================================
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#List of configuration options available.
|
2
|
+
Faceauth.configuration do |config|
|
3
|
+
# config.model_name = "user"
|
4
|
+
# config.uploader_name = "carrierwave"
|
5
|
+
# config.redirect_url = "root_path"
|
6
|
+
# config.findface_api_key = "YOUR_API_KEY"
|
7
|
+
# config.signup_picture_column = "user_picture"
|
8
|
+
# config.signin_picture_column = "last_sign_in_picture"
|
9
|
+
# config.email_column = "email"
|
10
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: faceauth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sandeep Mallela a.k.a Sam
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.0.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.0.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: findface
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.0.1
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.0.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: jpeg_camera
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.3.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.3.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mini_magick
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 4.6.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 4.6.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.9.8
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.9.8
|
83
|
+
description: A simple rails engine to implement authentication using Facial recognition
|
84
|
+
system
|
85
|
+
email:
|
86
|
+
- opensource@rubyeffect.com
|
87
|
+
- sandeep@rubyeffect.com
|
88
|
+
executables: []
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- MIT-LICENSE
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- app/assets/config/faceauth_manifest.js
|
96
|
+
- app/assets/images/faceauth/loader.gif
|
97
|
+
- app/assets/javascripts/faceauth/application.js
|
98
|
+
- app/assets/javascripts/faceauth/faces.js
|
99
|
+
- app/assets/javascripts/faceauth/jpeg_camera.swf
|
100
|
+
- app/assets/javascripts/faceauth/jpeg_camera_with_dependencies.min.js
|
101
|
+
- app/assets/javascripts/faceauth/shutter.mp3
|
102
|
+
- app/assets/javascripts/faceauth/shutter.ogg
|
103
|
+
- app/assets/stylesheets/faceauth/application.css
|
104
|
+
- app/assets/stylesheets/faceauth/faces.css
|
105
|
+
- app/controllers/faceauth/application_controller.rb
|
106
|
+
- app/controllers/faceauth/faces_controller.rb
|
107
|
+
- app/helpers/faceauth/application_helper.rb
|
108
|
+
- app/helpers/faceauth/faces_helper.rb
|
109
|
+
- app/jobs/faceauth/application_job.rb
|
110
|
+
- app/mailers/faceauth/application_mailer.rb
|
111
|
+
- app/models/faceauth/application_record.rb
|
112
|
+
- app/views/faceauth/faces/new.html.erb
|
113
|
+
- app/views/layouts/faceauth/application.html.erb
|
114
|
+
- config/routes.rb
|
115
|
+
- lib/faceauth.rb
|
116
|
+
- lib/faceauth/authenticate.rb
|
117
|
+
- lib/faceauth/configuration.rb
|
118
|
+
- lib/faceauth/engine.rb
|
119
|
+
- lib/faceauth/gem_dependencies.rb
|
120
|
+
- lib/faceauth/version.rb
|
121
|
+
- lib/generators/faceauth/install_generator.rb
|
122
|
+
- lib/generators/faceauth/views_generator.rb
|
123
|
+
- lib/generators/templates/README
|
124
|
+
- lib/generators/templates/initializer.rb
|
125
|
+
- lib/tasks/faceauth_tasks.rake
|
126
|
+
homepage: https://github.com/rubyeffect/faceauth
|
127
|
+
licenses:
|
128
|
+
- MIT
|
129
|
+
metadata: {}
|
130
|
+
post_install_message:
|
131
|
+
rdoc_options: []
|
132
|
+
require_paths:
|
133
|
+
- lib
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
requirements: []
|
145
|
+
rubyforge_project:
|
146
|
+
rubygems_version: 2.6.8
|
147
|
+
signing_key:
|
148
|
+
specification_version: 4
|
149
|
+
summary: A rails plugin that eliminates the use of passwords to sign in replacing
|
150
|
+
the medium with Human face
|
151
|
+
test_files: []
|