diff --git a/Gemfile b/Gemfile index c5c5d02..f9aa233 100644 --- a/Gemfile +++ b/Gemfile @@ -44,3 +44,6 @@ group :development do # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' end + +# Use Sorcery as authentication +gem 'sorcery' diff --git a/Gemfile.lock b/Gemfile.lock index 1820616..4921245 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -37,6 +37,7 @@ GEM thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) arel (6.0.3) + bcrypt (3.1.11) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) builder (3.2.2) @@ -52,6 +53,8 @@ GEM debug_inspector (0.0.2) erubis (2.7.0) execjs (2.7.0) + faraday (0.9.2) + multipart-post (>= 1.2, < 3) globalid (0.3.7) activesupport (>= 4.1.0) i18n (0.7.0) @@ -63,6 +66,7 @@ GEM railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (1.8.3) + jwt (1.5.6) loofah (2.0.3) nokogiri (>= 1.5.9) mail (2.6.4) @@ -73,9 +77,18 @@ GEM mini_portile2 (2.1.0) minitest (5.9.1) multi_json (1.12.1) + multi_xml (0.5.5) + multipart-post (2.0.0) mysql2 (0.3.21) nokogiri (1.6.8.1) mini_portile2 (~> 2.1.0) + oauth (0.5.1) + oauth2 (1.2.0) + faraday (>= 0.8, < 0.10) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) rack (1.6.4) rack-test (0.6.3) rack (>= 1.0) @@ -116,6 +129,10 @@ GEM sdoc (0.4.2) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) + sorcery (0.9.1) + bcrypt (~> 3.1) + oauth (~> 0.4, >= 0.4.4) + oauth2 (>= 0.8.0) spring (2.0.0) activesupport (>= 4.2) sprockets (3.7.0) @@ -153,6 +170,7 @@ DEPENDENCIES rails (= 4.2.4) sass-rails (~> 5.0) sdoc (~> 0.4.0) + sorcery spring turbolinks uglifier (>= 1.3.0) diff --git a/app/assets/javascripts/user_sessions.coffee b/app/assets/javascripts/user_sessions.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/user_sessions.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/users.coffee b/app/assets/javascripts/users.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/users.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/scaffolds.scss b/app/assets/stylesheets/scaffolds.scss new file mode 100644 index 0000000..ed7a765 --- /dev/null +++ b/app/assets/stylesheets/scaffolds.scss @@ -0,0 +1,73 @@ +body { + background-color: #fff; + color: #333; + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { + color: #000; + + &:visited { + color: #666; + } + + &:hover { + color: #fff; + background-color: #000; + } +} + +div { + &.field, &.actions { + margin-bottom: 10px; + } +} + +#notice { + color: green; +} + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px; + padding-bottom: 0; + margin-bottom: 20px; + background-color: #f0f0f0; + + h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + margin-bottom: 0px; + background-color: #c00; + color: #fff; + } + + ul li { + font-size: 12px; + list-style: square; + } +} diff --git a/app/assets/stylesheets/user_sessions.scss b/app/assets/stylesheets/user_sessions.scss new file mode 100644 index 0000000..69017e2 --- /dev/null +++ b/app/assets/stylesheets/user_sessions.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the UserSessions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.scss new file mode 100644 index 0000000..1efc835 --- /dev/null +++ b/app/assets/stylesheets/users.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the users controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d83690e..5dc1605 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,4 +2,10 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + before_action :require_login + + private + def not_authenticated + redirect_to login_path, alert: "Please login first" + end end diff --git a/app/controllers/user_sessions_controller.rb b/app/controllers/user_sessions_controller.rb new file mode 100644 index 0000000..00a6eaa --- /dev/null +++ b/app/controllers/user_sessions_controller.rb @@ -0,0 +1,21 @@ +class UserSessionsController < ApplicationController + skip_before_action :require_login, except: [:destroy] + + def new + @user = User.new + end + + def create + if @user = login(params[:email], params[:password]) + redirect_back_or_to(:users, notice: 'Login successful') + else + flash.now[:alert] = 'Login failed' + render action: 'new' + end + end + + def destroy + logout + redirect_to(:users, notice: 'Logged out!') + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..52aaaeb --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,75 @@ +class UsersController < ApplicationController + before_action :set_user, only: [:show, :edit, :update, :destroy] + skip_before_action :require_login, only: [:index, :new, :create] + + # GET /users + # GET /users.json + def index + @users = User.all + end + + # GET /users/1 + # GET /users/1.json + def show + end + + # GET /users/new + def new + @user = User.new + end + + # GET /users/1/edit + def edit + end + + # POST /users + # POST /users.json + def create + @user = User.new(user_params) + + respond_to do |format| + if @user.save + format.html { redirect_to(:users, notice: 'User was successfully created') } + format.json { render :show, status: :created, location: @user } + else + format.html { render :new } + format.json { render json: @user.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /users/1 + # PATCH/PUT /users/1.json + def update + respond_to do |format| + if @user.update(user_params) + format.html { redirect_to @user, notice: 'User was successfully updated.' } + format.json { render :show, status: :ok, location: @user } + else + format.html { render :edit } + format.json { render json: @user.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /users/1 + # DELETE /users/1.json + def destroy + @user.destroy + respond_to do |format| + format.html { redirect_to users_url, notice: 'User was successfully destroyed.' } + format.json { head :no_content } + end + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_user + @user = User.find(params[:id]) + end + + # Never trust parameters from the scary internet, only allow the white list through. + def user_params + params.require(:user).permit(:email, :password, :password_confirmation) + end +end diff --git a/app/helpers/user_sessions_helper.rb b/app/helpers/user_sessions_helper.rb new file mode 100644 index 0000000..2018402 --- /dev/null +++ b/app/helpers/user_sessions_helper.rb @@ -0,0 +1,2 @@ +module UserSessionsHelper +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..2310a24 --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,2 @@ +module UsersHelper +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..915d77a --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,9 @@ +class User < ActiveRecord::Base + authenticates_with_sorcery! + + validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] } + validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] } + validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] } + + validates :email, uniqueness: true +end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 40c5f30..4b40618 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -7,6 +7,19 @@ <%= csrf_meta_tags %>
+ +<%= flash[:notice] %>
+<%= flash[:alert] %>
+Find me in app/views/user_sessions/create.html.erb
diff --git a/app/views/user_sessions/destroy.html.erb b/app/views/user_sessions/destroy.html.erb new file mode 100644 index 0000000..50ebdef --- /dev/null +++ b/app/views/user_sessions/destroy.html.erb @@ -0,0 +1,2 @@ +Find me in app/views/user_sessions/destroy.html.erb
diff --git a/app/views/user_sessions/new.html.erb b/app/views/user_sessions/new.html.erb new file mode 100644 index 0000000..0ee7f92 --- /dev/null +++ b/app/views/user_sessions/new.html.erb @@ -0,0 +1,5 @@ +<%= notice %>
+ +Crypted password | +Salt | ++ | |||
---|---|---|---|---|---|
<%= user.email %> | +<%= user.crypted_password %> | +<%= user.salt %> | +<%= link_to 'Show', user %> | +<%= link_to 'Edit', edit_user_path(user) %> | +<%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %> | +
<%= notice %>
+ ++ Email: + <%= @user.email %> +
+ ++ Crypted password: + <%= @user.crypted_password %> +
+ ++ Salt: + <%= @user.salt %> +
+ +<%= link_to 'Edit', edit_user_path(@user) %> | +<%= link_to 'Back', users_path %> diff --git a/app/views/users/show.json.jbuilder b/app/views/users/show.json.jbuilder new file mode 100644 index 0000000..a2e64e0 --- /dev/null +++ b/app/views/users/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "users/user", user: @user \ No newline at end of file diff --git a/config/initializers/sorcery.rb b/config/initializers/sorcery.rb new file mode 100644 index 0000000..7c6b537 --- /dev/null +++ b/config/initializers/sorcery.rb @@ -0,0 +1,463 @@ +# The first thing you need to configure is which modules you need in your app. +# The default is nothing which will include only core features (password encryption, login/logout). +# Available submodules are: :user_activation, :http_basic_auth, :remember_me, +# :reset_password, :session_timeout, :brute_force_protection, :activity_logging, :external +Rails.application.config.sorcery.submodules = [] + +# Here you can configure each submodule's features. +Rails.application.config.sorcery.configure do |config| + # -- core -- + # What controller action to call for non-authenticated users. You can also + # override the 'not_authenticated' method of course. + # Default: `:not_authenticated` + # + # config.not_authenticated_action = + + + # When a non logged in user tries to enter a page that requires login, save + # the URL he wanted to reach, and send him there after login, using 'redirect_back_or_to'. + # Default: `true` + # + # config.save_return_to_url = + + + # Set domain option for cookies; Useful for remember_me submodule. + # Default: `nil` + # + # config.cookie_domain = + + + # Allow the remember_me cookie to be set through AJAX + # Default: `true` + # + # config.remember_me_httponly = + + + # -- session timeout -- + # How long in seconds to keep the session alive. + # Default: `3600` + # + # config.session_timeout = + + + # Use the last action as the beginning of session timeout. + # Default: `false` + # + # config.session_timeout_from_last_action = + + + # -- http_basic_auth -- + # What realm to display for which controller name. For example {"My App" => "Application"} + # Default: `{"application" => "Application"}` + # + # config.controller_to_realm_map = + + + # -- activity logging -- + # will register the time of last user login, every login. + # Default: `true` + # + # config.register_login_time = + + + # will register the time of last user logout, every logout. + # Default: `true` + # + # config.register_logout_time = + + + # will register the time of last user action, every action. + # Default: `true` + # + # config.register_last_activity_time = + + + # -- external -- + # What providers are supported by this app, i.e. [:twitter, :facebook, :github, :linkedin, :xing, :google, :liveid, :salesforce] . + # Default: `[]` + # + # config.external_providers = + + + # You can change it by your local ca_file. i.e. '/etc/pki/tls/certs/ca-bundle.crt' + # Path to ca_file. By default use a internal ca-bundle.crt. + # Default: `'path/to/ca_file'` + # + # config.ca_file = + + + # For information about LinkedIn API: + # - user info fields go to https://developer.linkedin.com/documents/profile-fields + # - access permissions go to https://developer.linkedin.com/documents/authentication#granting + # + # config.linkedin.key = "" + # config.linkedin.secret = "" + # config.linkedin.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=linkedin" + # config.linkedin.user_info_fields = ['first-name', 'last-name'] + # config.linkedin.user_info_mapping = {first_name: "firstName", last_name: "lastName"} + # config.linkedin.access_permissions = ['r_basicprofile'] + # + # + # For information about XING API: + # - user info fields go to https://dev.xing.com/docs/get/users/me + # + # config.xing.key = "" + # config.xing.secret = "" + # config.xing.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=xing" + # config.xing.user_info_mapping = {first_name: "first_name", last_name: "last_name"} + # + # + # Twitter will not accept any requests nor redirect uri containing localhost, + # make sure you use 0.0.0.0:3000 to access your app in development + # + # config.twitter.key = "" + # config.twitter.secret = "" + # config.twitter.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=twitter" + # config.twitter.user_info_mapping = {:email => "screen_name"} + # + # config.facebook.key = "" + # config.facebook.secret = "" + # config.facebook.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=facebook" + # config.facebook.user_info_mapping = {:email => "name"} + # config.facebook.access_permissions = ["email", "publish_actions"] + # config.facebook.display = "page" + # config.facebook.api_version = "v2.2" + # + # config.github.key = "" + # config.github.secret = "" + # config.github.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=github" + # config.github.user_info_mapping = {:email => "name"} + # + # config.google.key = "" + # config.google.secret = "" + # config.google.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=google" + # config.google.user_info_mapping = {:email => "email", :username => "name"} + # + # config.vk.key = "" + # config.vk.secret = "" + # config.vk.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=vk" + # config.vk.user_info_mapping = {:login => "domain", :name => "full_name"} + # + # To use liveid in development mode you have to replace mydomain.com with + # a valid domain even in development. To use a valid domain in development + # simply add your domain in your /etc/hosts file in front of 127.0.0.1 + # + # config.liveid.key = "" + # config.liveid.secret = "" + # config.liveid.callback_url = "http://mydomain.com:3000/oauth/callback?provider=liveid" + # config.liveid.user_info_mapping = {:username => "name"} + + # For information about JIRA API: + # https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Example+-+OAuth+authentication + # to obtain the consumer key and the public key you can use the jira-ruby gem https://github.com/sumoheavy/jira-ruby + # or run openssl req -x509 -nodes -newkey rsa:1024 -sha1 -keyout rsakey.pem -out rsacert.pem to obtain the public key + # Make sure you have configured the application link properly + + # config.jira.key = "1234567" + # config.jira.secret = "jiraTest" + # config.jira.site = "http://localhost:2990/jira/plugins/servlet/oauth" + # config.jira.signature_method = "RSA-SHA1" + # config.jira.private_key_file = "rsakey.pem" + + # For information about Salesforce API: + # https://developer.salesforce.com/signup & + # https://www.salesforce.com/us/developer/docs/api_rest/ + # Salesforce callback_url must be https. You can run the following to generate self-signed ssl cert + # openssl req -new -newkey rsa:2048 -sha1 -days 365 -nodes -x509 -keyout server.key -out server.crt + # Make sure you have configured the application link properly + # config.salesforce.key = '123123' + # config.salesforce.secret = 'acb123' + # config.salesforce.callback_url = "https://127.0.0.1:9292/oauth/callback?provider=salesforce" + # config.salesforce.scope = "full" + # config.salesforce.user_info_mapping = {:email => "email"} + + # --- user config --- + config.user_config do |user| + # -- core -- + # specify username attributes, for example: [:username, :email]. + # Default: `[:email]` + # + # user.username_attribute_names = + + + # change *virtual* password attribute, the one which is used until an encrypted one is generated. + # Default: `:password` + # + # user.password_attribute_name = + + + # downcase the username before trying to authenticate, default is false + # Default: `false` + # + # user.downcase_username_before_authenticating = + + + # change default email attribute. + # Default: `:email` + # + # user.email_attribute_name = + + + # change default crypted_password attribute. + # Default: `:crypted_password` + # + # user.crypted_password_attribute_name = + + + # what pattern to use to join the password with the salt + # Default: `""` + # + # user.salt_join_token = + + + # change default salt attribute. + # Default: `:salt` + # + # user.salt_attribute_name = + + + # how many times to apply encryption to the password. + # Default: `nil` + # + # user.stretches = + + + # encryption key used to encrypt reversible encryptions such as AES256. + # WARNING: If used for users' passwords, changing this key will leave passwords undecryptable! + # Default: `nil` + # + # user.encryption_key = + + + # use an external encryption class. + # Default: `nil` + # + # user.custom_encryption_provider = + + + # encryption algorithm name. See 'encryption_algorithm=' for available options. + # Default: `:bcrypt` + # + # user.encryption_algorithm = + + + # make this configuration inheritable for subclasses. Useful for ActiveRecord's STI. + # Default: `false` + # + # user.subclasses_inherit_config = + + + # -- remember_me -- + # How long in seconds the session length will be + # Default: `604800` + # + # user.remember_me_for = + + + # -- user_activation -- + # the attribute name to hold activation state (active/pending). + # Default: `:activation_state` + # + # user.activation_state_attribute_name = + + + # the attribute name to hold activation code (sent by email). + # Default: `:activation_token` + # + # user.activation_token_attribute_name = + + + # the attribute name to hold activation code expiration date. + # Default: `:activation_token_expires_at` + # + # user.activation_token_expires_at_attribute_name = + + + # how many seconds before the activation code expires. nil for never expires. + # Default: `nil` + # + # user.activation_token_expiration_period = + + + # your mailer class. Required. + # Default: `nil` + # + # user.user_activation_mailer = + + + # when true sorcery will not automatically + # email activation details and allow you to + # manually handle how and when email is sent. + # Default: `false` + # + # user.activation_mailer_disabled = + + + # activation needed email method on your mailer class. + # Default: `:activation_needed_email` + # + # user.activation_needed_email_method_name = + + + # activation success email method on your mailer class. + # Default: `:activation_success_email` + # + # user.activation_success_email_method_name = + + + # do you want to prevent or allow users that did not activate by email to login? + # Default: `true` + # + # user.prevent_non_active_users_to_login = + + + # -- reset_password -- + # reset password code attribute name. + # Default: `:reset_password_token` + # + # user.reset_password_token_attribute_name = + + + # expires at attribute name. + # Default: `:reset_password_token_expires_at` + # + # user.reset_password_token_expires_at_attribute_name = + + + # when was email sent, used for hammering protection. + # Default: `:reset_password_email_sent_at` + # + # user.reset_password_email_sent_at_attribute_name = + + + # mailer class. Needed. + # Default: `nil` + # + # user.reset_password_mailer = + + + # reset password email method on your mailer class. + # Default: `:reset_password_email` + # + # user.reset_password_email_method_name = + + + # when true sorcery will not automatically + # email password reset details and allow you to + # manually handle how and when email is sent + # Default: `false` + # + # user.reset_password_mailer_disabled = + + + # how many seconds before the reset request expires. nil for never expires. + # Default: `nil` + # + # user.reset_password_expiration_period = + + + # hammering protection, how long in seconds to wait before allowing another email to be sent. + # Default: `5 * 60` + # + # user.reset_password_time_between_emails = + + + # -- brute_force_protection -- + # Failed logins attribute name. + # Default: `:failed_logins_count` + # + # user.failed_logins_count_attribute_name = + + + # This field indicates whether user is banned and when it will be active again. + # Default: `:lock_expires_at` + # + # user.lock_expires_at_attribute_name = + + + # How many failed logins allowed. + # Default: `50` + # + # user.consecutive_login_retries_amount_limit = + + + # How long the user should be banned. in seconds. 0 for permanent. + # Default: `60 * 60` + # + # user.login_lock_time_period = + + # Unlock token attribute name + # Default: `:unlock_token` + # + # user.unlock_token_attribute_name = + + # Unlock token mailer method + # Default: `:send_unlock_token_email` + # + # user.unlock_token_email_method_name = + + # when true sorcery will not automatically + # send email with unlock token + # Default: `false` + # + # user.unlock_token_mailer_disabled = true + + # Unlock token mailer class + # Default: `nil` + # + # user.unlock_token_mailer = UserMailer + + # -- activity logging -- + # Last login attribute name. + # Default: `:last_login_at` + # + # user.last_login_at_attribute_name = + + + # Last logout attribute name. + # Default: `:last_logout_at` + # + # user.last_logout_at_attribute_name = + + + # Last activity attribute name. + # Default: `:last_activity_at` + # + # user.last_activity_at_attribute_name = + + + # How long since last activity is the user defined logged out? + # Default: `10 * 60` + # + # user.activity_timeout = + + + # -- external -- + # Class which holds the various external provider data for this user. + # Default: `nil` + # + # user.authentications_class = + + + # User's identifier in authentications class. + # Default: `:user_id` + # + # user.authentications_user_id_attribute_name = + + + # Provider's identifier in authentications class. + # Default: `:provider` + # + # user.provider_attribute_name = + + + # User's external unique identifier in authentications class. + # Default: `:uid` + # + # user.provider_uid_attribute_name = + end + + # This line must come after the 'user config' block. + # Define which model authenticates with sorcery. + config.user_class = "User" +end diff --git a/config/routes.rb b/config/routes.rb index 3f66539..f3403ba 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,10 @@ Rails.application.routes.draw do + root :to => 'users#index' + resources :user_sessions + resources :users + + get 'login' => 'user_sessions#new', :as => :login + post 'logout' => 'user_sessions#destroy', :as => :logout # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes". diff --git a/db/migrate/20161024063842_sorcery_core.rb b/db/migrate/20161024063842_sorcery_core.rb new file mode 100644 index 0000000..5eedcb0 --- /dev/null +++ b/db/migrate/20161024063842_sorcery_core.rb @@ -0,0 +1,13 @@ +class SorceryCore < ActiveRecord::Migration + def change + create_table :users do |t| + t.string :email, :null => false + t.string :crypted_password + t.string :salt + + t.timestamps + end + + add_index :users, :email, unique: true + end +end \ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..365b0b5 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,26 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20161024063842) do + + create_table "users", force: :cascade do |t| + t.string "email", limit: 255, null: false + t.string "crypted_password", limit: 255 + t.string "salt", limit: 255 + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree + +end