faceauth 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +171 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/config/faceauth_manifest.js +2 -0
  6. data/app/assets/images/faceauth/loader.gif +0 -0
  7. data/app/assets/javascripts/faceauth/application.js +15 -0
  8. data/app/assets/javascripts/faceauth/faces.js +177 -0
  9. data/app/assets/javascripts/faceauth/jpeg_camera.swf +0 -0
  10. data/app/assets/javascripts/faceauth/jpeg_camera_with_dependencies.min.js +12 -0
  11. data/app/assets/javascripts/faceauth/shutter.mp3 +0 -0
  12. data/app/assets/javascripts/faceauth/shutter.ogg +0 -0
  13. data/app/assets/stylesheets/faceauth/application.css +17 -0
  14. data/app/assets/stylesheets/faceauth/faces.css +59 -0
  15. data/app/controllers/faceauth/application_controller.rb +7 -0
  16. data/app/controllers/faceauth/faces_controller.rb +44 -0
  17. data/app/helpers/faceauth/application_helper.rb +4 -0
  18. data/app/helpers/faceauth/faces_helper.rb +4 -0
  19. data/app/jobs/faceauth/application_job.rb +4 -0
  20. data/app/mailers/faceauth/application_mailer.rb +6 -0
  21. data/app/models/faceauth/application_record.rb +5 -0
  22. data/app/views/faceauth/faces/new.html.erb +22 -0
  23. data/app/views/layouts/faceauth/application.html.erb +23 -0
  24. data/config/routes.rb +4 -0
  25. data/lib/faceauth.rb +17 -0
  26. data/lib/faceauth/authenticate.rb +26 -0
  27. data/lib/faceauth/configuration.rb +28 -0
  28. data/lib/faceauth/engine.rb +12 -0
  29. data/lib/faceauth/gem_dependencies.rb +3 -0
  30. data/lib/faceauth/version.rb +3 -0
  31. data/lib/generators/faceauth/install_generator.rb +19 -0
  32. data/lib/generators/faceauth/views_generator.rb +34 -0
  33. data/lib/generators/templates/README +34 -0
  34. data/lib/generators/templates/initializer.rb +10 -0
  35. data/lib/tasks/faceauth_tasks.rake +4 -0
  36. metadata +151 -0
@@ -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
@@ -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.
@@ -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
@@ -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
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/faceauth .js
2
+ //= link_directory ../stylesheets/faceauth .css
@@ -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
+ });
@@ -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);
@@ -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,7 @@
1
+ module Faceauth
2
+ class ApplicationController < ActionController::Base
3
+ #protect_from_forgery with: :exception
4
+ protect_from_forgery with: :null_session
5
+
6
+ end
7
+ end
@@ -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,4 @@
1
+ module Faceauth
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Faceauth
2
+ module FacesHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Faceauth
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Faceauth
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Faceauth
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ 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
+ &mdash; 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>
@@ -0,0 +1,4 @@
1
+ Faceauth::Engine.routes.draw do
2
+ resources :faces
3
+ get '/faceauth/sign_in', to: 'faces#new', as: 'new_session'
4
+ end
@@ -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,3 @@
1
+ #Gem dependencies
2
+ require 'devise'
3
+ require 'faceauth'
@@ -0,0 +1,3 @@
1
+ module Faceauth
2
+ VERSION = '0.0.1'
3
+ end
@@ -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
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :faceauth do
3
+ # # Task goes here
4
+ # 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: []