diff --git a/.gitignore b/.gitignore index 54ad3a5..95ef1e3 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ !/log/.keep /tmp /coverage +/primes.csv* +.env diff --git a/Gemfile b/Gemfile index cac44ca..129122d 100644 --- a/Gemfile +++ b/Gemfile @@ -25,7 +25,8 @@ gem 'sdoc', '~> 0.4.0', group: :doc # gem 'bcrypt', '~> 3.1.7' # Use Unicorn as the app server -# gem 'unicorn' +gem 'unicorn' +gem 'rails_12factor', group: :production # Use Capistrano for deployment # gem 'capistrano-rails', group: :development diff --git a/Gemfile.lock b/Gemfile.lock index f35dc90..25ac67a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,6 +97,7 @@ GEM railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (1.8.2) + kgio (2.9.3) launchy (2.4.3) addressable (~> 2.3) loofah (2.0.1) @@ -140,11 +141,17 @@ GEM rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.1) loofah (~> 2.0) + rails_12factor (0.0.3) + rails_serve_static_assets + rails_stdout_logging + rails_serve_static_assets (0.0.3) + rails_stdout_logging (0.0.3) railties (4.2.0) actionpack (= 4.2.0) activesupport (= 4.2.0) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) + raindrops (0.13.0) rake (10.4.2) rdoc (4.2.0) json (~> 1.4) @@ -208,6 +215,10 @@ GEM uglifier (2.7.0) execjs (>= 0.3.0) json (>= 1.8.0) + unicorn (4.8.3) + kgio (~> 2.6) + rack + raindrops (~> 0.7) valid_attribute (2.0.0) warden (1.2.3) rack (>= 1.0) @@ -237,11 +248,13 @@ DEPENDENCIES pg pry rails (= 4.2.0) + rails_12factor rspec-rails sass-rails (~> 5.0) sdoc (~> 0.4.0) shoulda-matchers spring uglifier (>= 1.3.0) + unicorn valid_attribute web-console (~> 2.0) diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..9c82374 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb diff --git a/README.rdoc b/README.rdoc index dd4e97e..835f5bd 100644 --- a/README.rdoc +++ b/README.rdoc @@ -1,28 +1,12 @@ == README +http://pssst-staging.herokuapp.com -This README would normally document whatever steps are necessary to get the -application up and running. +Psst! is a social networking site with a twist. Users can make friends and +create both public posts and secret messages. Secret messages are encrypted +posts, directed at a specific user. Anyone who can see your posts can see the +secret messages you send in their encrypted state, but only the intended viewer +can decrypt and read them. The site uses RSA public key encryption. -Things you may want to cover: - -* Ruby version - -* System dependencies - -* Configuration - -* Database creation - -* Database initialization - -* How to run the test suite - -* Services (job queues, cache servers, search engines, etc.) - -* Deployment instructions - -* ... - - -Please feel free to use a different markup language if you do not plan to run -rake doc:app. +RSA encryption captured my interest when I learned about it in my number theory +class, and when thinking about things I wanted to program, it came to mind. +Coding the encryption and decryption algorithms was an exciting challenge. diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 2132fee..d3f5b6d 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -28,7 +28,23 @@ float: left; } .post_header{ - color: red; - background-color: black; - border: 1px black solid; + color: white; + background-color: #393636; + padding: 10px 10px 2px 5px; + font-family: 'Playfair Display'; + font-size: 14pt; +} +.post_header a{ + color: white; +} +.post_header a:hover{ + color: teal; +} +.text_body{ + font-family: 'Roboto Condensed'; + padding: 10px; + +} +.panel{ + } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f07ea34..9af312c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -16,7 +16,11 @@ def configure_permitted_parameters :email, :password, :password_confirmation, - :remember_me + :remember_me, + :secret_key_p, + :secret_key_q, + :public_key_m, + :public_key_k ) end diff --git a/app/controllers/friendships_controller.rb b/app/controllers/friendships_controller.rb index bbd63a1..dea8f77 100644 --- a/app/controllers/friendships_controller.rb +++ b/app/controllers/friendships_controller.rb @@ -4,7 +4,7 @@ def create @friendship = current_user.friendships.build(friend: @new_friend) if @friendship.save flash[:notice] = "You have successfully added #{@new_friend.username} as a - friend. We'll let you know when they confirm your friendship" + friend." redirect_to root_path end diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb new file mode 100644 index 0000000..db9231c --- /dev/null +++ b/app/controllers/messages_controller.rb @@ -0,0 +1,110 @@ +def confirmed_friends(user) + confirmed_friends = [] + user.friendships.each do |friendship| + if friendship.confirmed? + confirmed_friends << friendship.friend + end + end + user.inverse_friendships.each do |friendship| + if friendship.confirmed? + confirmed_friends << friendship.user + end + end + confirmed_friends +end + +class MessagesController < ApplicationController + include EncryptionHelper + + before_action :require_secret_keys, only: [:show] + + def new + authenticate_user! + @user = current_user + @message = Message.new + @recipient_options = confirmed_friends(current_user) + end + + def create + authenticate_user! + @recipient_options = confirmed_friends(current_user) + @message = Message.new(message_params) + + if @message.valid? && + @message.public_key_m == @message.recipient.public_key_m && + @message.public_key_k == @message.recipient.public_key_k + @message.body = encrypt(@message.body, + @message.public_key_m, + @message.public_key_k) + end + + @message.sender = current_user + + if @message.public_key_m != @message.recipient.public_key_m || + @message.public_key_k != @message.recipient.public_key_k + flash[:error] = "Public keys must match recipient's public keys" + render :new + elsif @message.save + flash[:notice] = "Your message has been sent." + redirect_to root_path + else + render :new + end + end + + def index + authenticate_user! + @user = current_user + @secret_key_p = current_user.secret_key_p + @secret_key_q = current_user.secret_key_q + @messages = [] + Message.all.order(:created_at).reverse_order.each do |message| + if message.recipient == current_user + @messages << message + end + end + end + + def show + authenticate_user! + @message = Message.find(params[:id]) + @message_body = @message.body + @p = @message.recipient.secret_key_p + @q = @message.recipient.secret_key_q + @m = @message.recipient.public_key_m + @k = @message.recipient.public_key_k + @user = current_user + @recipient = @message.recipient + end + + private + + def message_params + params.require(:message).permit(:body, + :recipient_id, + :public_key_m, + :public_key_k) + end + + def require_secret_keys + if current_user + @user = current_user + @secret_key_p = current_user.secret_key_p + @secret_key_q = current_user.secret_key_q + @messages = [] + Message.all.order(:created_at).reverse_order.each do |message| + if message.recipient == current_user + @messages << message + end + end + if !params[:secret_key_p] || + !params[:secret_key_q] || + params[:secret_key_p].to_i != @secret_key_p || + params[:secret_key_q].to_i != @secret_key_q + + flash[:error] = "Secret keys are required" + render :index + end + end + end +end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 45ea8f1..cd8ec9c 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -1,11 +1,26 @@ -require "datetime" class PostsController < ApplicationController + include PostHelper def index if !current_user redirect_to new_user_session_path else @post = Post.new - @posts = Post.all.order(:created_at).reverse_order + @posts = [] + @messages = [] + Post.all.order(:created_at).reverse_order.each do |post| + if friends_post?(post) + @posts << post + elsif post.user == current_user + @posts << post + end + end + Message.all.order(:created_at).reverse_order.each do |message| + if friends_message?(message) + @messages << message + elsif message.sender == current_user + @messages << message + end + end end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 0000000..eee9058 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,53 @@ +module ApplicationHelper + def split(datetime) + datetime.split("-") + end + + def month(datetime) + months = { + "01" => "January", + "02" => "February", + "03" => "March", + "04" => "April", + "05" => "May", + "06" => "June", + "07" => "July", + "08" => "August", + "09" => "September", + "10" => "October", + "11" => "November", + "12" => "December", + } + months[split(datetime)[1]] + end + + def day(datetime) + split(datetime)[2].split(" ")[0] + end + + def year(datetime) + split(datetime)[0] + end + + def time(datetime) + split_time = split(datetime)[2].split(" ")[1].split(":") + time = [] + hour = split_time[0].to_i % 12 + if hour == 0 + hour = 12 + end + time << hour + time << split_time[1] + if split_time[0].to_i < 12 + time << "am" + else + time << "pm" + end + time + end + + def format_date(datetime) + "#{month(datetime)} #{day(datetime)}, #{year(datetime)} at + #{time(datetime)[0]}:#{time(datetime)[1]}#{time(datetime)[2]}" + end +end diff --git a/app/helpers/encryption_helper.rb b/app/helpers/encryption_helper.rb new file mode 100644 index 0000000..d142000 --- /dev/null +++ b/app/helpers/encryption_helper.rb @@ -0,0 +1,191 @@ +require "csv" + +module EncryptionHelper + def assign_secret_key + primes = [] + + CSV.foreach("primes_test.csv") do |row| + primes << row.first.to_i + end + + length = primes.length - 1 + + key = [primes[rand(length)], primes[rand(length)]] + + while key[0] == key[1] + key = [primes[rand(length)], primes[rand(length)]] + end + + key + end + + def euclidean_algorithm(a, b) + if b > a + placeholder_a = a + a = b + b = placeholder_a + end + euclidean_algorithm = [[a, a / b, b, a % b]] + n = 0 + while euclidean_algorithm[n][3] > 0 + iteration = [euclidean_algorithm[n][2], + euclidean_algorithm[n][2] / euclidean_algorithm[n][3], + euclidean_algorithm[n][3], + euclidean_algorithm[n][2] % euclidean_algorithm[n][3]] + euclidean_algorithm << iteration + + n += 1 + end + euclidean_algorithm + end + + def extended_euclidean_algorithm(phi_of_m, k) + u_array = [0, 1] + q_array = [] + euclidean_algorithm(phi_of_m, k).each do |iter| + q_array << iter[1] + end + q_array.pop + + q_array.each_with_index do |q, i| + u_array << ((u_array[i] - u_array[i + 1] * q) % phi_of_m) + end + u_array.last + end + + def gcd(a, b) + e_a = euclidean_algorithm(a, b) + if e_a.length > 1 + e_a[e_a.length - 2][3] + else + e_a[0][2] + end + end + + def calculate_public_key(p, q) + m = p * q + phi_of_m = (p - 1) * (q - 1) + + k = rand(10000000) + + while gcd(phi_of_m, k) != 1 + k = rand(10000000) + end + + [m, k] + end + + def encoder_hash + { "a" => "11", "b" => "12", "c" => "13", "d" => "14", "e" => "15", + "f" => "16", "g" => "17", "h" => "18", "i" => "19", "j" => "20", + "k" => "21", "l" => "22", "m" => "23", "n" => "24", "o" => "25", + "p" => "26", "q" => "27", "r" => "28", "s" => "29", "t" => "30", + "u" => "31", "v" => "32", "w" => "33", "x" => "34", "y" => "35", + "z" => "36" + } + end + + def decoder_hash + decoder_hash = {} + + encoder_hash.each do |k, v| + decoder_hash[v] = k + end + decoder_hash + end + + def encode(message) + # strips message and puts each letter into it's own space in an array + message_array = message.downcase.gsub(/[^a-z]/, "").split("") + encoded_message = [] + message_array.each do |letter| + encoded_message << encoder_hash[letter] + end + encoded_message.join("") + # returns encoded message as a string of digits + end + + def split_message(message, m) + x = 0 + split_message = [] + length = m.to_s.length - 1 + while x < message.length + split_message << message.slice(x, length) + x += (length) + end + split_message + end + + def successive_squaring(number, m, k) + x_array = [] + while k > 0 + x = Math.log2(k).to_i + x_array << x + k -= 2**x + end + + successive_squaring = [number.to_i] + x = x_array.first + x.times do + number = number.to_i**2 % m + successive_squaring << number + end + + product = 1 + + x_array.each do |y| + product *= successive_squaring[y] + product %= m + end + product + end + + def encrypt(message, m, k) + message = encode(message) + # splits string into an array of |number|s which are m-1 digits long + split_message = split_message(message, m) + + encrypted_message_array = [] + + # raises each of these numbers to the kth power mod m (using successive + # squaring) and puts them into an array + split_message.each do |piece| + n = successive_squaring(piece, m, k) + encrypted_message_array << n + end + encrypted_message_array.join(", ") + end + + # def create_gobilty_gook(encrypted_message) + # # takes encrypted message and splits it into an array with two digits in + # # each space + # # maybe using a loop and slice + # # loops through array and creates new array using decoder_hash + # # returns gobilty_gook + # end + + def decrypt(message, p, q, m, k) + message = message.split(", ") + phi_of_m = (p - 1) * (q - 1) + u = extended_euclidean_algorithm(phi_of_m, k) + + decrypted = [] + message.each do |piece| + decrypted << successive_squaring(piece.to_i, m, u) + end + + decrypted_string = decrypted.join("") + x = 0 + split_message = [] + while x < decrypted_string.length + split_message << decrypted_string.slice(x, 2) + x += 2 + end + + decoded_message = [] + split_message.each do |number| + decoded_message << decoder_hash[number] + end + decoded_message.join("") + end +end diff --git a/app/helpers/post_helper.rb b/app/helpers/post_helper.rb new file mode 100644 index 0000000..c14811b --- /dev/null +++ b/app/helpers/post_helper.rb @@ -0,0 +1,29 @@ +module PostHelper + def friends_post?(post) + Friendship.where(user: current_user, confirmed: true).each do |friendship| + if post.user == friendship.friend + return true + end + end + Friendship.where(friend: current_user, confirmed: true).each do |friendship| + if post.user == friendship.user + return true + end + end + false + end + + def friends_message?(message) + Friendship.where(user: current_user, confirmed: true).each do |friendship| + if message.sender == friendship.friend + return true + end + end + Friendship.where(friend: current_user, confirmed: true).each do |friendship| + if message.sender == friendship.user + return true + end + end + false + end +end diff --git a/app/models/message.rb b/app/models/message.rb new file mode 100644 index 0000000..886e493 --- /dev/null +++ b/app/models/message.rb @@ -0,0 +1,9 @@ +class Message < ActiveRecord::Base + belongs_to :sender, class_name: "User" + belongs_to :recipient, class_name: "User" + + validates :body, presence: true, length: { minimum: 100 } + validates :public_key_m, presence: true + validates :public_key_k, presence: true + validates :recipient, presence: true +end diff --git a/app/models/user.rb b/app/models/user.rb index 6c5c395..5ea5c09 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,10 +1,14 @@ class User < ActiveRecord::Base - has_many :friendships + has_many :friendships, dependent: :destroy has_many :friends, through: :friendships has_many :inverse_friendships, class_name: "Friendship", - foreign_key: "friend_id" + foreign_key: "friend_id", + dependent: :destroy has_many :inverse_friends, through: :inverse_friendships, source: :user + has_many :posts, dependent: :destroy + has_many :messages, foreign_key: "sender_id" + has_many :messages, foreign_key: "recipient_id" # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index 1a1eb54..18b1379 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -11,6 +11,13 @@ <%= devise_error_messages! %> + <% p_q = assign_secret_key %> + <%= f.hidden_field :secret_key_p, value: p_q[0] %> + <%= f.hidden_field :secret_key_q, value: p_q[1] %> + + <% m_k = calculate_public_key(p_q[0], p_q[1]) %> + <%= f.hidden_field :public_key_m, value: m_k[0] %> + <%= f.hidden_field :public_key_k, value: m_k[1] %>
diff --git a/app/views/friendships/index.html.erb b/app/views/friendships/index.html.erb index a31e931..7b32101 100644 --- a/app/views/friendships/index.html.erb +++ b/app/views/friendships/index.html.erb @@ -14,8 +14,8 @@
  • - <%= "#{ friend[0].first_name } #{ friend[0].last_name } - - #{friend[0].username}" %> + <%= link_to("#{ friend[0].first_name } #{ friend[0].last_name } - + #{friend[0].username}", user_path(friend)) %> <%= button_to "Remove #{friend[0].username} from friends", user_friendship_path(@user, friend[1]), method: :delete, class: "small button" %> @@ -56,8 +56,8 @@
  • - <%= "#{ friend[0].first_name } #{ friend[0].last_name } - - #{friend[0].username}" %> + <%= link_to("#{ friend[0].first_name } #{ friend[0].last_name } - + #{friend[0].username}", user_path(friend)) %> <%= form_tag user_friendship_path(@user, friend[1]), method: :patch do %> <%= hidden_field_tag :confirmed, true %> @@ -91,8 +91,8 @@
  • - <%= "#{ friend[0].first_name } #{ friend[0].last_name } - - #{friend[0].username}" %> + <%= link_to("#{ friend[0].first_name } #{ friend[0].last_name } - + #{friend[0].username}", user_path(friend)) %>
  • diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 1753a06..05f1746 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -3,6 +3,8 @@ Psst <%= stylesheet_link_tag 'application', media: 'all' %> + + <%= csrf_meta_tags %> @@ -17,11 +19,15 @@
    <% if signed_in? %>
      +
    • <%= link_to "Feed", posts_path %>
    • <%= link_to "Friends", user_friendships_path(current_user) %>
    • +
    • <%= link_to "Secret Messages", new_message_path %>
    • +
    • <%= link_to "Mailbox", messages_path %>
    <% end %>
      <%- if current_user -%> +
    • <%= link_to current_user.username, user_path(current_user) %>
    • <%= link_to 'Sign Out', destroy_user_session_path, method: :delete %>
    • <%- else -%>
    • <%= link_to 'Sign Up', new_user_registration_path %>
    • diff --git a/app/views/messages/index.html.erb b/app/views/messages/index.html.erb new file mode 100644 index 0000000..4dbbfce --- /dev/null +++ b/app/views/messages/index.html.erb @@ -0,0 +1,59 @@ +
      +
      +
      +

      Mailbox

      +
      +
      +
      + +
      + + +
      +
      +

      Secret Keys

      +
      +
      +
      p: <%= @user.secret_key_p %>
      +
      +
      +
      q: <%= @user.secret_key_q %>
      +
      +
      +
      +
      + + +
      + + <% if @messages %> + <% @messages.each do |message| %> +
      +
      +

      <%= message.sender.username %>

      +
      <%= format_date(message.created_at.to_s) %>
      +
      +
      +
      +
      + @<%= message.recipient.username %> + <%= message.body %> +
      +
      + <%= form_tag(message_path(message), method: :get) do %> +
      + <%= label_tag (:secret_key_p) %> + <%= text_field_tag(:secret_key_p) %> +
      +
      + <%= label_tag (:secret_key_q) %> + <%= text_field_tag(:secret_key_q) %> +
      + <%= submit_tag "Decrypt", class: "small button" %> + <% end %> +
      +
      + <% end %> + <% end %> +
      + diff --git a/app/views/messages/new.html.erb b/app/views/messages/new.html.erb new file mode 100644 index 0000000..0a6ccd8 --- /dev/null +++ b/app/views/messages/new.html.erb @@ -0,0 +1,49 @@ +
      +
      +
      +

      Secret Message Creator

      +
      +
      +
      +<% if @message.errors.any? %> +
        + <% @message.errors.full_messages.each do |msg| %> +
        +
        +
      • <%= msg %>
      • +
        +
        + <% end %> +
      +<% end %> +
      +
      + <%= form_for @message do |f| %> +
      + <%= f.label :recipient %> + <%= f.collection_select(:recipient_id, @recipient_options, :id, :username) %> +
      +
      + + <%= f.text_area :body, placeholder: "Enter your secret message (must be at least 100 characters)" %> +
      +
      + <%= f.label :public_key_m %> + <%= f.text_field :public_key_m, placeholder: "Find this on your friend's profile page" %> +
      +
      + <%= f.label :public_key_k %> + <%= f.text_field :public_key_k, placeholder: "Find this on you friend's profile page" %> +
      +
      +

      Please do not include confidential information. + Encryption is for educational purposes, and is not secure.

      +
      +
      + <%= f.submit "Send", class: "small button" %> +
      + <% end %> +
      +
      diff --git a/app/views/messages/show.html.erb b/app/views/messages/show.html.erb new file mode 100644 index 0000000..c524576 --- /dev/null +++ b/app/views/messages/show.html.erb @@ -0,0 +1,31 @@ +<% if @user != @recipient %> +
      +
      +

      You are not authorized to view this page

      +
      +
      +<% else %> +
      +
      +
      +

      Your Secret Message Revealed

      +
      +
      +
      +
      +
      +
      +
      + <%= link_to(@message.sender.username, user_path(@message.sender)) %> +
      +
      <%= format_date(@message.created_at.to_s) %>
      +
      +
      + +
      + @<%= @message.recipient.username %> + <%= decrypt(@message_body, @p, @q, @m, @k) %> +
      +
      +
      +<% end %> diff --git a/app/views/posts/index.html.erb b/app/views/posts/index.html.erb index d73c138..db15503 100644 --- a/app/views/posts/index.html.erb +++ b/app/views/posts/index.html.erb @@ -17,28 +17,52 @@ - <%= f.text_area :body %> + <%= f.text_area :body, placeholder: "Say something" %>
    - <%= f.submit "Submit" %> + <%= f.submit "Submit", class: "small button"%>
    <% end %>
    +<% if @messages %> +<% @messages.each do |message| %> +
    +
    +
    +
    +
    + <%= link_to(message.sender.username, user_path(message.sender)) %> +
    +
    <%= format_date(message.created_at.to_s) %>
    +
    +
    +
    +
    + @<%= message.recipient.username %> + <%= message.body %> +
    +
    +
    +
    +<% end %> +<% end %> <% if @posts %> <% @posts.each do |post| %>
    -
    +
    <%= post.user.username %>
    <%= format_date(post.created_at.to_s) %>
    - <%= post.body %> +
    + <%= post.body %> +
    diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 2bebe54..735fb20 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -14,3 +14,15 @@ <% end %>
    + +
    +
    + <% if current_user.friends.include?(@user) || + current_user.inverse_friends.include?(@user) || + @user == current_user %> +

    Public Keys

    +

    m: <%= @user.public_key_m %>

    +

    k: <%= @user.public_key_k %>

    + <% end %> +
    +
    diff --git a/config/routes.rb b/config/routes.rb index 672cc7b..3f29752 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,13 +1,15 @@ Rails.application.routes.draw do - root 'homes#index' + root "posts#index" devise_for :users resources :friendships, only: [:create] resources :users, only: [:index, :show] do resources :friendships, only: [:index, :update, :destroy] + # resources :messages, only: [:index] end + resources :messages, only: [:new, :create, :show, :index] resources :posts, only: [:index, :create] # The priority is based upon order of creation: first created -> highest priority. diff --git a/config/unicorn.rb b/config/unicorn.rb new file mode 100644 index 0000000..2cafd94 --- /dev/null +++ b/config/unicorn.rb @@ -0,0 +1,22 @@ +worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3) +timeout 15 +preload_app true + +before_fork do |server, worker| + Signal.trap 'TERM' do + puts 'Unicorn master intercepting TERM and sending myself QUIT instead' + Process.kill 'QUIT', Process.pid + end + + defined?(ActiveRecord::Base) and + ActiveRecord::Base.connection.disconnect! +end + +after_fork do |server, worker| + Signal.trap 'TERM' do + puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT' + end + + defined?(ActiveRecord::Base) and + ActiveRecord::Base.establish_connection +end diff --git a/db/migrate/20150121185804_add_secret_key_column_to_user.rb b/db/migrate/20150121185804_add_secret_key_column_to_user.rb new file mode 100644 index 0000000..2160b4d --- /dev/null +++ b/db/migrate/20150121185804_add_secret_key_column_to_user.rb @@ -0,0 +1,8 @@ +class AddSecretKeyColumnToUser < ActiveRecord::Migration + def change + add_column :users, :secret_key_p, :integer + add_column :users, :secret_key_q, :integer + add_column :users, :public_key_m, :integer + add_column :users, :public_key_k, :integer + end +end diff --git a/db/migrate/20150121224147_change_data_type_for_keys.rb b/db/migrate/20150121224147_change_data_type_for_keys.rb new file mode 100644 index 0000000..3ce1f75 --- /dev/null +++ b/db/migrate/20150121224147_change_data_type_for_keys.rb @@ -0,0 +1,15 @@ +class ChangeDataTypeForKeys < ActiveRecord::Migration + def up + change_column :users, :public_key_m, :integer, limit: 8 + change_column :users, :public_key_k, :integer, limit: 8 + change_column :users, :secret_key_p, :integer, limit: 8 + change_column :users, :secret_key_q, :integer, limit: 8 + end + + def down + change_column :users, :public_key_m, :integer, limit: nil + change_column :users, :public_key_k, :integer, limit: nil + change_column :users, :secret_key_p, :integer, limit: nil + change_column :users, :secret_key_q, :integer, limit: nil + end +end diff --git a/db/migrate/20150122162304_create_messages.rb b/db/migrate/20150122162304_create_messages.rb new file mode 100644 index 0000000..3a788d6 --- /dev/null +++ b/db/migrate/20150122162304_create_messages.rb @@ -0,0 +1,12 @@ +class CreateMessages < ActiveRecord::Migration + def change + create_table :messages do |t| + t.integer :sender_id, null: false + t.integer :recipient_id, null: false + t.integer :public_key_m, limit: 8, null: false + t.integer :public_key_k, limit: 8, null: false + t.text :body, null: false + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index c0496d4..0162818 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150116182710) do +ActiveRecord::Schema.define(version: 20150122162304) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -24,6 +24,16 @@ t.datetime "updated_at" end + create_table "messages", force: :cascade do |t| + t.integer "sender_id", null: false + t.integer "recipient_id", null: false + t.integer "public_key_m", limit: 8, null: false + t.integer "public_key_k", limit: 8, null: false + t.text "body", null: false + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "posts", force: :cascade do |t| t.string "body", null: false t.integer "user_id", null: false @@ -32,21 +42,25 @@ end create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false + t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.inet "current_sign_in_ip" t.inet "last_sign_in_ip" t.datetime "created_at" t.datetime "updated_at" - t.string "username", null: false - t.string "first_name", null: false - t.string "last_name", null: false + t.string "username", null: false + t.string "first_name", null: false + t.string "last_name", null: false + t.integer "secret_key_p", limit: 8 + t.integer "secret_key_q", limit: 8 + t.integer "public_key_m", limit: 8 + t.integer "public_key_k", limit: 8 end add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree diff --git a/lib/datetime.rb b/lib/datetime.rb deleted file mode 100644 index f904f0f..0000000 --- a/lib/datetime.rb +++ /dev/null @@ -1,47 +0,0 @@ -def split(datetime) - datetime.split("-") -end - -def month(datetime) - months = { - "01" => "January", - "02" => "February", - "03" => "March", - "04" => "April", - "05" => "May", - "06" => "June", - "07" => "July", - "08" => "August", - "09" => "September", - "10" => "October", - "11" => "November", - "12" => "December", - } - months[split(datetime)[1]] -end - -def day(datetime) - split(datetime)[2].split(" ")[0] -end - -def year(datetime) - split(datetime)[0] -end - -def time(datetime) - split_time = split(datetime)[2].split(" ")[1].split(":") - time = [] - time << split_time[0].to_i % 12 - time << split_time[1] - if split_time[0].to_i <= 12 - time << "am" - else - time << "pm" - end - time -end - -def format_date(datetime) - "#{month(datetime)} #{day(datetime)}, #{year(datetime)} at - #{time(datetime)[0]}:#{time(datetime)[1]}#{time(datetime)[2]}" -end diff --git a/primes_test.csv b/primes_test.csv new file mode 100644 index 0000000..1a55757 --- /dev/null +++ b/primes_test.csv @@ -0,0 +1,1428 @@ +171070199 +171070241 +171070247 +171070253 +171070259 +171070279 +171070283 +171070301 +171070309 +171070381 +171070447 +171070451 +171070457 +171070477 +171070513 +171070519 +171070561 +171070567 +171070579 +171070583 +171070589 +171070597 +171070607 +171070621 +171070633 +171070639 +171070649 +171070733 +171070759 +171070799 +171070829 +171070841 +171070843 +171070859 +171070891 +171070897 +171070937 +171070961 +171070967 +171070969 +171070979 +171070997 +171071009 +171071063 +171071081 +171071093 +171071099 +171071101 +171071123 +171071129 +171071161 +171071177 +171071179 +171071207 +171071209 +171071291 +171071317 +171071359 +171071429 +171071437 +171071441 +171071443 +171071471 +171071521 +171071543 +171071581 +171071591 +171071669 +171071689 +171071711 +171071713 +171071729 +171071737 +171071741 +171071767 +171071821 +171071827 +171071843 +171071851 +171071897 +171071917 +171071933 +171071941 +171071947 +171071951 +171071959 +171071993 +171072001 +171072007 +171072019 +171072059 +171072061 +171072101 +171072107 +171072113 +171072119 +171072133 +171072149 +171072179 +171072197 +171072203 +171072221 +171072227 +171072263 +171072281 +171072283 +171072289 +171072301 +171072337 +171072359 +171072431 +171072437 +171072439 +171072449 +171072467 +171072491 +171072511 +171072521 +171072529 +171072553 +171072569 +171072613 +171072647 +171072659 +171072661 +171072667 +171072677 +171072697 +171072739 +171072743 +171072757 +171072761 +171072763 +171072773 +171072829 +171072833 +171072851 +171072857 +171072871 +171072893 +171072899 +171072931 +171072947 +171072949 +171072961 +171072977 +171073039 +171073051 +171073061 +171073069 +171073099 +171073121 +171073129 +171073153 +171073181 +171073183 +171073213 +171073223 +171073247 +171073267 +171073271 +171073289 +171073319 +171073339 +171073351 +171073367 +171073387 +171073411 +171073423 +171073433 +171073439 +171073447 +171073451 +171073457 +171073481 +171073489 +171073589 +171073603 +171073613 +171073619 +171073621 +171073687 +171073691 +171073711 +171073723 +171073757 +171073787 +171073807 +171073831 +171073883 +171073897 +171073913 +171073921 +171073927 +171073933 +171073943 +171073961 +171073963 +171073979 +171073993 +171073999 +171074011 +171074053 +171074063 +171074093 +171074107 +171074117 +171074129 +171074171 +171074177 +171074191 +171074231 +171074251 +171074257 +171074269 +171074297 +171074311 +171074327 +171074329 +171074353 +171074359 +171074377 +171074401 +171074411 +171074413 +171074419 +171074443 +171074459 +171074521 +171074539 +171074557 +171074609 +171074623 +171074627 +171074653 +171074663 +171074671 +171074677 +171074699 +171074719 +171074747 +171074749 +171074801 +171074867 +171074909 +171074921 +171074929 +171074933 +171074977 +171074987 +171075011 +171075061 +171075101 +171075109 +171075167 +171075181 +171075193 +171075209 +171075241 +171075257 +171075271 +171075283 +171075287 +171075313 +171075323 +171075341 +171075353 +171075407 +171075413 +171075419 +171075461 +171075473 +171075479 +171075497 +171075503 +171075523 +171075529 +171075551 +171075581 +171075613 +171075617 +171075623 +171075647 +171075683 +171075689 +171075703 +171075713 +171075733 +171075757 +171075761 +171075767 +171075769 +171075787 +171075797 +171075799 +171075803 +171075841 +171075851 +171075871 +171075881 +171075899 +171075917 +171075923 +171075929 +171075941 +171075953 +171075967 +171075979 +171076007 +171076013 +171076021 +171076033 +171076039 +171076043 +171076051 +171076063 +171076069 +171076091 +171076127 +171076153 +171076181 +171076183 +171076189 +171076261 +171076291 +171076309 +171076319 +171076327 +171076331 +171076337 +171076343 +171076351 +171076379 +171076387 +171076429 +171076441 +171076447 +171076469 +171076471 +171076483 +171076537 +171076603 +171076621 +171076649 +171076651 +171076657 +171076667 +171076679 +171076681 +171076687 +171076709 +171076721 +171076733 +171076751 +171076753 +171076793 +171076811 +171076819 +171076837 +171076849 +171076859 +171076889 +171076897 +171076907 +171076943 +171076963 +171076979 +171076987 +171076993 +171077009 +171077017 +171077021 +171077047 +171077077 +171077087 +171077099 +171077117 +171077147 +171077149 +171077161 +171077183 +171077191 +171077219 +171077237 +171077279 +171077299 +171077303 +171077317 +171077383 +171077419 +171077429 +171077449 +171077483 +171077497 +171077503 +171077513 +171077527 +171077549 +171077563 +171077573 +171077591 +171077629 +171077663 +171077671 +171077689 +171077723 +171077737 +171077741 +171077759 +171077771 +171077773 +171077801 +171077827 +171077831 +171077839 +171077891 +171077903 +171077911 +171077923 +171077969 +171077971 +171077983 +171077999 +171078013 +171078031 +171078043 +171078053 +171078059 +171078067 +171078077 +171078091 +171078097 +171078169 +171078179 +171078197 +171078239 +171078317 +171078331 +171078353 +171078359 +171078379 +171078389 +171078409 +171078419 +171078431 +171078433 +171078461 +171078473 +171078487 +171078539 +171078631 +171078643 +171078653 +171078659 +171078697 +171078703 +171078727 +171078751 +171078757 +171078763 +171078767 +171078781 +171078821 +171078833 +171078881 +171078883 +171078893 +171078961 +171078967 +171079001 +171079063 +171079079 +171079081 +171079087 +171079099 +171079121 +171079123 +171079141 +171079147 +171079169 +171079213 +171079219 +171079243 +171079261 +171079301 +171079333 +171079339 +171079357 +171079367 +171079417 +171079457 +171079459 +171079487 +171079501 +171079507 +171079529 +171079537 +171079547 +171079549 +171079561 +171079567 +171079583 +171079631 +171079637 +171079663 +171079697 +171079723 +171079739 +171079801 +171079817 +171079841 +171079939 +171079943 +171079973 +171079981 +171080023 +171080029 +171080057 +171080069 +171080071 +171080081 +171080087 +171080089 +171080093 +171080111 +171080137 +171080153 +171080171 +171080183 +171080191 +171080207 +171080213 +171080243 +171080291 +171080293 +171080317 +171080323 +171080381 +171080383 +171080407 +171080431 +171080443 +171080453 +171080461 +171080467 +171080473 +171080477 +171080491 +171080501 +171080519 +171080521 +171080527 +171080561 +171080573 +171080587 +171080597 +171080627 +171080641 +171080671 +171080713 +171080719 +171080753 +171080759 +171080771 +171080813 +171080849 +171080851 +171080857 +171080867 +171080869 +171080939 +171080953 +171080957 +171080963 +171080981 +171081023 +171081049 +171081067 +171081089 +171081101 +171081109 +171081161 +171081167 +171081181 +171081193 +171081203 +171081221 +171081227 +171081241 +171081283 +171081289 +171081341 +171081367 +171081371 +171081401 +171081419 +171081431 +171081433 +171081439 +171081473 +171081481 +171081497 +171081499 +171081539 +171081553 +171081587 +171081593 +171081601 +171081611 +171081679 +171081709 +171081749 +171081767 +171081787 +171081791 +171081793 +171081857 +171081877 +171081893 +171081913 +171081917 +171081919 +171081923 +171081973 +171082001 +171082013 +171082031 +171082057 +171082111 +171082127 +171082141 +171082147 +171082151 +171082157 +171082159 +171082207 +171082229 +171082267 +171082277 +171082313 +171082319 +171082447 +171082453 +171082481 +171082493 +171082501 +171082507 +171082519 +171082559 +171082591 +171082601 +171082603 +171082609 +171082619 +171082643 +171082661 +171082663 +171082673 +171082697 +171082721 +171082739 +171082741 +171082759 +171082787 +171082799 +171082817 +171082819 +171082823 +171082871 +171082903 +171082907 +171082909 +171082913 +171082921 +171082927 +171082957 +171082969 +171082981 +171082999 +171083027 +171083047 +171083051 +171083069 +171083083 +171083089 +171083117 +171083137 +171083161 +171083167 +171083173 +171083179 +171083183 +171083207 +171083219 +171083221 +171083261 +171083273 +171083281 +171083299 +171083323 +171083347 +171083377 +171083387 +171083399 +171083401 +171083443 +171083483 +171083503 +171083509 +171083519 +171083527 +171083531 +171083567 +171083573 +171083599 +171083621 +171083663 +171083669 +171083677 +171083681 +171083687 +171083701 +171083707 +171083741 +171083747 +171083753 +171083791 +171083813 +171083831 +171083849 +171083851 +171083873 +171083879 +171083909 +171083911 +171083917 +171083933 +171083981 +171083989 +171084013 +171084029 +171084061 +171084091 +171084101 +171084131 +171084139 +171084161 +171084163 +171084169 +171084181 +171084223 +171084241 +171084259 +171084289 +171084307 +171084317 +171084323 +171084343 +171084377 +171084383 +171084391 +171084421 +171084439 +171084467 +171084469 +171084499 +171084503 +171084509 +171084523 +171084527 +171084577 +171084581 +171084593 +171084619 +171084671 +171084673 +171084703 +171084709 +171084713 +171084737 +171084743 +171084763 +171084827 +171084839 +171084853 +171084871 +171084913 +171084959 +171084973 +171084989 +171085009 +171085021 +171085027 +171085037 +171085039 +171085067 +171085069 +171085081 +171085091 +171085097 +171085121 +171085153 +171085193 +171085199 +171085207 +171085279 +171085283 +171085301 +171085303 +171085307 +171085319 +171085351 +171085379 +171085391 +171085393 +171085469 +171085483 +171085489 +171085507 +171085511 +171085517 +171085531 +171085543 +171085549 +171085553 +171085571 +171085573 +171085583 +171085591 +171085597 +171085601 +171085619 +171085631 +171085657 +171085667 +171085697 +171085699 +171085711 +171085723 +171085741 +171085751 +171085769 +171085813 +171085829 +171085843 +171085853 +171085879 +171085903 +171085913 +171085919 +171085933 +171085963 +171085979 +171086009 +171086023 +171086057 +171086059 +171086081 +171086099 +171086107 +171086137 +171086143 +171086159 +171086161 +171086177 +171086189 +171086221 +171086233 +171086239 +171086243 +171086263 +171086299 +171086353 +171086359 +171086417 +171086441 +171086459 +171086467 +171086537 +171086561 +171086567 +171086603 +171086609 +171086651 +171086677 +171086681 +171086683 +171086731 +171086749 +171086777 +171086789 +171086791 +171086807 +171086813 +171086819 +171086837 +171086869 +171086873 +171086893 +171086897 +171086939 +171086941 +171086957 +171086983 +171086989 +171086999 +171087019 +171087053 +171087071 +171087073 +171087089 +171087101 +171087121 +171087143 +171087151 +171087157 +171087181 +171087187 +171087251 +171087263 +171087277 +171087289 +171087307 +171087341 +171087353 +171087361 +171087401 +171087407 +171087461 +171087479 +171087509 +171087517 +171087569 +171087577 +171087641 +171087673 +171087689 +171087701 +171087713 +171087751 +171087769 +171087773 +171087797 +171087811 +171087821 +171087877 +171087883 +171087887 +171087893 +171087899 +171087901 +171087937 +171087941 +171087953 +171087967 +171087979 +171088019 +171088033 +171088063 +171088067 +171088079 +171088087 +171088097 +171088109 +171088123 +171088147 +171088189 +171088199 +171088213 +171088249 +171088277 +171088297 +171088303 +171088339 +171088343 +171088361 +171088417 +171088433 +171088439 +171088447 +171088501 +171088553 +171088571 +171088579 +171088583 +171088607 +171088633 +171088649 +171088657 +171088699 +171088727 +171088739 +171088747 +171088763 +171088793 +171088807 +171088831 +171088837 +171088919 +171088937 +171088949 +171088961 +171088963 +171088987 +171088991 +171088993 +171088997 +171089027 +171089029 +171089059 +171089069 +171089117 +171089143 +171089153 +171089159 +171089179 +171089189 +171089209 +171089263 +171089267 +171089291 +171089293 +171089363 +171089393 +171089419 +171089423 +171089461 +171089467 +171089491 +171089519 +171089521 +171089543 +171089549 +171089551 +171089579 +171089593 +171089603 +171089609 +171089621 +171089629 +171089669 +171089741 +171089753 +171089767 +171089771 +171089827 +171089839 +171089873 +171089903 +171089909 +171089923 +171089953 +171089959 +171089977 +171089981 +171089987 +171089993 +171090001 +171090019 +171090043 +171090047 +171090067 +171090071 +171090137 +171090149 +171090151 +171090203 +171090223 +171090229 +171090233 +171090247 +171090281 +171090287 +171090341 +171090347 +171090349 +171090389 +171090391 +171090407 +171090461 +171090467 +171090509 +171090511 +171090523 +171090529 +171090547 +171090589 +171090631 +171090641 +171090659 +171090671 +171090677 +171090697 +171090761 +171090823 +171090863 +171090883 +171090893 +171090911 +171090919 +171090929 +171090947 +171090949 +171090979 +171091013 +171091027 +171091033 +171091057 +171091099 +171091117 +171091127 +171091163 +171091177 +171091199 +171091211 +171091223 +171091229 +171091241 +171091253 +171091259 +171091279 +171091289 +171091307 +171091309 +171091313 +171091321 +171091339 +171091351 +171091373 +171091381 +171091399 +171091423 +171091433 +171091471 +171091513 +171091537 +171091541 +171091567 +171091577 +171091603 +171091639 +171091673 +171091699 +171091717 +171091721 +171091727 +171091769 +171091777 +171091813 +171091819 +171091829 +171091873 +171091889 +171091913 +171091951 +171091981 +171091993 +171091997 +171092011 +171092023 +171092027 +171092099 +171092111 +171092113 +171092123 +171092137 +171092143 +171092161 +171092171 +171092197 +171092209 +171092231 +171092237 +171092269 +171092279 +171092287 +171092293 +171092323 +171092347 +171092371 +171092401 +171092417 +171092429 +171092447 +171092479 +171092483 +171092491 +171092497 +171092501 +171092527 +171092531 +171092557 +171092563 +171092567 +171092609 +171092633 +171092683 +171092717 +171092723 +171092767 +171092807 +171092819 +171092821 +171092833 +171092863 +171092881 +171092903 +171092923 +171092927 +171092939 +171092951 +171093007 +171093017 +171093023 +171093029 +171093037 +171093047 +171093059 +171093073 +171093077 +171093079 +171093089 +171093101 +171093161 +171093163 +171093191 +171093211 +171093283 +171093317 +171093319 +171093323 +171093331 +171093361 +171093383 +171093401 +171093421 +171093427 +171093443 +171093449 +171093469 +171093473 +171093479 +171093493 +171093497 +171093509 +171093551 +171093553 +171093563 +171093581 +171093617 +171093631 +171093641 +171093653 +171093667 +171093691 +171093719 +171093739 +171093751 +171093787 +171093827 +171093833 +171093847 +171093877 +171093961 +171093971 +171093973 +171093977 +171093983 +171094003 +171094039 +171094061 +171094073 +171094117 +171094141 +171094151 +171094153 +171094163 +171094169 +171094199 +171094201 +171094207 +171094219 +171094243 +171094267 +171094279 +171094283 +171094289 +171094331 +171094333 +171094337 +171094393 +171094421 +171094453 +171094471 +171094481 +171094507 +171094537 +171094559 +171094571 +171094597 +171094607 +171094619 +171094633 +171094643 +171094657 +171094669 +171094739 +171094741 +171094787 +171094799 +171094801 +171094841 +171094849 +171094853 +171094883 +171094933 +171094969 +171094997 +171095003 +171095027 +171095083 +171095129 +171095167 +171095203 +171095207 +171095213 +171095219 +171095233 +171095251 +171095269 +171095279 +171095297 +171095311 +171095329 +171095347 +171095357 +171095371 +171095377 +171095383 +171095387 +171095401 +171095489 +171095521 +171095527 +171095569 +171095591 +171095609 +171095641 +171095653 +171095671 +171095677 +171095719 +171095723 +171095731 +171095759 +171095761 +171095777 +171095801 +171095831 +171095833 +171095843 +171095863 +171095917 +171095921 +171095927 +171095941 +171095959 +171095963 +171095971 +171095987 +171095999 +171096017 +171096019 +171096043 +171096061 +171096071 +171096077 +171096113 +171096131 +171096151 +171096173 +171096187 +171096193 +171096209 +171096221 +171096227 +171096229 +171096271 +171096293 +171096329 +171096353 +171096371 +171096377 +171096397 +171096413 +171096421 +171096461 +171096467 +171096469 +171096487 +171096503 +171096509 +171096553 +171096571 +171096641 +171096647 +171096661 +171096683 +171096689 +171096703 +171096733 +171096749 +171096791 +171096797 +171096799 +171096811 +171096851 +171096853 +171096859 +171096869 +171096883 +171096941 +171096943 +171096949 +171097001 +171097019 +171097021 +171097037 diff --git a/spec/features/friendships/user_adds_a_friend_from_users_page_spec.rb b/spec/features/friendships/user_adds_a_friend_from_users_page_spec.rb index 6125709..c3c75df 100644 --- a/spec/features/friendships/user_adds_a_friend_from_users_page_spec.rb +++ b/spec/features/friendships/user_adds_a_friend_from_users_page_spec.rb @@ -40,8 +40,7 @@ click_on "Add #{user2.username} as a friend" expect(page).to have_content "You have successfully added #{ - user2.username} as a friend. We'll let you know when they confirm your - friendship" + user2.username} as a friend." visit users_path @@ -58,8 +57,7 @@ click_on "Add friend" expect(page).to have_content "You have successfully added #{ - user2.username} as a friend. We'll let you know when they confirm your - friendship" + user2.username} as a friend." end scenario "User cannot add a friend who has already been added" do diff --git a/spec/features/messages/user_creates_a_new_message_spec.rb b/spec/features/messages/user_creates_a_new_message_spec.rb new file mode 100644 index 0000000..b95712f --- /dev/null +++ b/spec/features/messages/user_creates_a_new_message_spec.rb @@ -0,0 +1,157 @@ +require "rails_helper" + +feature "User creates a new message", %{ + As a user + I want to create a message + So that I can send a message to my friend +} do + + # Acceptance Criteria + # [] I must be logged in + # [x] I must specify a receiver + # [x] The message receiver must be a confirmed friend or an inverse friend + # [x] I can choose a receiver from a drop-down menu + # [x] I must provide a message with at least 100 characters + # [x] I must specify a public key m and a public key k + # [x] The public keys specified must match the message receiver's public keys + # [] When I click send, I receive an alert that my message is not secure, and + # the option to cancel or continue + # [x] When I send a message, I am given a success message + # [x] If I am unable to send a message, I can see a list of errors + # [x] My encrypted message appears on my posts feed + # [] My encrypted message appears on my friends' posts feeds + # [] My encrypted message appears on the receiver's profile page + + before(:each) do + @user = FactoryGirl.create(:user) + @user.secret_key_p = 171078169 + @user.secret_key_q = 171079817 + @user.public_key_m = 29268021845215073 + @user.public_key_k = 5339869 + @user.save + end + + context "authenticated user" do + before(:each) do + @user1 = FactoryGirl.create(:user) + + visit root_path + + click_on "Sign In" + + fill_in "Login", with: @user1.email + fill_in "Password", with: @user1.password + click_on "Log in" + end + + scenario "user sends a message to a friend" do + + message = "This message will be over one-hundred characters + long if I just keep typing until it reaches at least one-hundred + characters and then I keep typing a little bit longer." + + Friendship.create(user: @user1, friend: @user, confirmed: true) + + visit new_message_path + + find("#recipient").select(@user.username) + fill_in "Body", with: message + fill_in "Public key m", with: @user.public_key_m + fill_in "Public key k", with: @user.public_key_k + click_on "Send" + # click_on "Continue" + + expect(page).to have_content "Your message has been sent" + expect(page).to have_content "13987979245948945, 6597549169661591, + 19501227974711707, 15878093936820780, 17120061003915500, 1674708906257554, + 19207038272600258, 5842957003381056, 9179485880262553, 19536289285048631, + 2786926719715928, 11025250514274376, 17197200100095568, 29250927161084515, + 13819283828387603, 15802654134488562, 21826030828151967, + 25410511871589372" + end + + scenario "user sends a message to an inverse friend" do + message = "This message will be over one-hundred characters + long if I just keep typing until it reaches at least one-hundred + characters and then I keep typing a little bit longer." + + Friendship.create(user: @user, friend: @user1, confirmed: true) + + visit new_message_path + + find("#recipient").select(@user.username) + fill_in "Body", with: message + fill_in "Public key m", with: @user.public_key_m + fill_in "Public key k", with: @user.public_key_k + click_on "Send" + + expect(page).to have_content "Your message has been sent" + expect(page).to have_content "13987979245948945, 6597549169661591, + 19501227974711707, 15878093936820780, 17120061003915500, 1674708906257554, + 19207038272600258, 5842957003381056, 9179485880262553, 19536289285048631, + 2786926719715928, 11025250514274376, 17197200100095568, 29250927161084515, + 13819283828387603, 15802654134488562, 21826030828151967, + 25410511871589372" + end + + scenario "user doesn't input body" do + + Friendship.create(user: @user, friend: @user1, confirmed: true) + + visit new_message_path + + fill_in "Public key m", with: @user.public_key_m + fill_in "Public key k", with: @user.public_key_k + click_on "Send" + + expect(page).to have_content "Body can't be blank" + end + + scenario "user enters the wrong public key" do + message = "This message will be over one-hundred characters + long if I just keep typing until it reaches at least one-hundred + characters and then I keep typing a little bit longer." + + Friendship.create(user: @user, friend: @user1, confirmed: true) + + visit new_message_path + + find("#recipient").select(@user.username) + fill_in "Body", with: message + fill_in "Public key m", with: @user.public_key_k + fill_in "Public key k", with: @user.public_key_k + click_on "Send" + + expect(page).to have_content "Public keys must match recipient's public" + end + + scenario "user cannot send message to a non-friend/inverse-friend" do + visit new_message_path + + expect(page).not_to have_select("message[recipient_id]", + options: [@user.username]) + end + + scenario "user cannot send a message to an unconfirmed friend" do + Friendship.create(user: @user1, friend: @user) + visit new_message_path + + expect(page).not_to have_select("message[recipient_id]", + options: [@user.username]) + end + + scenario "user cannot send a message to herself" do + Friendship.create(user: @user, friend: @user1, confirmed: true) + visit new_message_path + + expect(page).not_to have_select("message[recipient_id]", + options: [@user1.username]) + end + end + + scenario "unauthenticated user" do + visit new_message_path + + expect(page).to have_content "Log in" + end +end diff --git a/spec/features/messages/user_views_decrypted_message_spec.rb b/spec/features/messages/user_views_decrypted_message_spec.rb new file mode 100644 index 0000000..ef87a5a --- /dev/null +++ b/spec/features/messages/user_views_decrypted_message_spec.rb @@ -0,0 +1,95 @@ +require "rails_helper" + +feature "User views decrypted message", %{ + As a user + I want to view my decrypted message + So I can read what my friend has written to me +} do + + # Acceptance Criteria + # [] I must be logged in + # [] I must enter my secret key + # [] The secret key I enter must match my secret key + # [] I can only view messages of which I am the recipient. + # [] When I click on decrypt, I am taken to the decrypted message page + # [] The message I view is the message that was written to me, + # stripped of punctuation, spacing, and capitalization + + before(:each) do + @user = FactoryGirl.create(:user) + @user.secret_key_p = 171078169 + @user.secret_key_q = 171079817 + @user.public_key_m = 29268021845215073 + @user.public_key_k = 5339869 + @user.save + + @friend = FactoryGirl.create(:user) + Friendship.create(user: @user, friend: @friend, confirmed: true) + + message = "This message will be over one-hundred characters + long if I just keep typing until it reaches at least one-hundred + characters and then I keep typing a little bit longer." + + visit root_path + + click_on "Sign In" + fill_in "Login", with: @friend.email + fill_in "Password", with: @friend.password + click_on "Log in" + + visit new_message_path + + find("#recipient").select(@user.username) + fill_in "Body", with: message + fill_in "Public key m", with: @user.public_key_m + fill_in "Public key k", with: @user.public_key_k + click_on "Send" + click_on "Sign Out" + + visit root_path + + click_on "Sign In" + + fill_in "Login", with: @user.email + fill_in "Password", with: @user.password + click_on "Log in" + end + + scenario "User views her decrypted message" do + click_on "Mailbox" + fill_in "Secret key p", with: @user.secret_key_p + fill_in "Secret key q", with: @user.secret_key_q + click_on "Decrypt" + + expect(page).to have_content "thismessagewillbeoveronehundredcharacterslong" + end + + scenario "User enters incorrect secret key" do + click_on "Mailbox" + fill_in "Secret key p", with: @user.secret_key_p + fill_in "Secret key q", with: @user.secret_key_p + click_on "Decrypt" + + expect(page).not_to have_content " + thismessagewillbeoveronehundredcharacterslong" + end + + scenario "Non-recipient user cannot view decrypted message" do + message = Message.find_by(recipient: @user) + user = FactoryGirl.create(:user) + user.secret_key_p = 1234 + user.secret_key_q = 12345 + user.save + click_on "Sign Out" + click_on "Sign In" + + fill_in "Login", with: user.email + fill_in "Password", with: user.password + click_on "Log in" + + visit message_path(message) + + expect(page).not_to have_content "thismessagewillbeoveronehundredcharacters" + end + +end diff --git a/spec/features/messages/user_views_messages_spec.rb b/spec/features/messages/user_views_messages_spec.rb new file mode 100644 index 0000000..a1db760 --- /dev/null +++ b/spec/features/messages/user_views_messages_spec.rb @@ -0,0 +1,227 @@ +require "rails_helper" + +feature "User views encrypted messages", %{ + As a user + I want to show people my encrypted messages + So that my friend can feel special when they decrypt their message +} do + + # Acceptance Criteria + # [] I must be logged in + # [] I can see encrypted messages posted by all of my friends on my posts page + # [] I can see the name of the recipient + # [] I can see the date and time the message is posted + # [] If the recipient is me, I can see the message in my mailbox + # [] I can see my secret key in my mailbox + # [] I can decrypt the message in my mailbox + before(:each) do + @user = FactoryGirl.create(:user) + @user.secret_key_p = 171078169 + @user.secret_key_q = 171079817 + @user.public_key_m = 29268021845215073 + @user.public_key_k = 5339869 + @user.save + + @friend = FactoryGirl.create(:user) + Friendship.create(user: @user, friend: @friend, confirmed: true) + + message = "This message will be over one-hundred characters + long if I just keep typing until it reaches at least one-hundred + characters and then I keep typing a little bit longer." + + visit root_path + + click_on "Sign In" + fill_in "Login", with: @friend.email + fill_in "Password", with: @friend.password + click_on "Log in" + + visit new_message_path + + find("#recipient").select(@user.username) + fill_in "Body", with: message + fill_in "Public key m", with: @user.public_key_m + fill_in "Public key k", with: @user.public_key_k + click_on "Send" + click_on "Sign Out" + end + + scenario "User views a friend's encrypted message in feed" do + @user1 = FactoryGirl.create(:user) + + visit root_path + + click_on "Sign In" + + fill_in "Login", with: @user1.email + fill_in "Password", with: @user1.password + click_on "Log in" + + Friendship.create(user: @user1, friend: @friend, confirmed: true) + + click_on "Feed" + + expect(page).to have_content "13987979245948945, 6597549169661591, + 19501227974711707, 15878093936820780, + 17120061003915500, 1674708906257554, + 19207038272600258, 5842957003381056, + 9179485880262553, 19536289285048631, + 2786926719715928, 11025250514274376, + 17197200100095568, 29250927161084515, + 13819283828387603, 15802654134488562, + 21826030828151967, 25410511871589372" + expect(page).to have_content @user.username + end + + scenario "User views inverse-friend's encrypted message in feed" do + @user1 = FactoryGirl.create(:user) + + visit root_path + + click_on "Sign In" + + fill_in "Login", with: @user1.email + fill_in "Password", with: @user1.password + click_on "Log in" + + Friendship.create(user: @friend, friend: @user1, confirmed: true) + + click_on "Feed" + + expect(page).to have_content "13987979245948945, 6597549169661591, + 19501227974711707, 15878093936820780, + 17120061003915500, 1674708906257554, + 19207038272600258, 5842957003381056, + 9179485880262553, 19536289285048631, + 2786926719715928, 11025250514274376, + 17197200100095568, 29250927161084515, + 13819283828387603, 15802654134488562, + 21826030828151967, 25410511871589372" + expect(page).to have_content @user.username + end + + scenario "User views her own encrypted message in feed" do + visit root_path + + click_on "Sign In" + + fill_in "Login", with: @user.email + fill_in "Password", with: @user.password + click_on "Log in" + + click_on "Feed" + + expect(page).to have_content "13987979245948945, 6597549169661591, + 19501227974711707, 15878093936820780, + 17120061003915500, 1674708906257554, + 19207038272600258, 5842957003381056, + 9179485880262553, 19536289285048631, + 2786926719715928, 11025250514274376, + 17197200100095568, 29250927161084515, + 13819283828387603, 15802654134488562, + 21826030828151967, 25410511871589372" + expect(page).to have_content @user.username + end + + scenario "User views her own encrypted message in mailbox" do + visit root_path + + click_on "Sign In" + + fill_in "Login", with: @user.email + fill_in "Password", with: @user.password + click_on "Log in" + + click_on "Mailbox" + + expect(page).to have_content @user.secret_key_p + expect(page).to have_content @user.secret_key_q + expect(page).to have_content "13987979245948945, 6597549169661591, + 19501227974711707, 15878093936820780, + 17120061003915500, 1674708906257554, + 19207038272600258, 5842957003381056, + 9179485880262553, 19536289285048631, + 2786926719715928, 11025250514274376, + 17197200100095568, 29250927161084515, + 13819283828387603, 15802654134488562, + 21826030828151967, 25410511871589372" + expect(page).to have_content @user.username + expect(page).to have_button "Decrypt" + end + + scenario "User cannot view friend's encrypted message in mailbox" do + @user1 = FactoryGirl.create(:user) + + visit root_path + + click_on "Sign In" + + fill_in "Login", with: @user1.email + fill_in "Password", with: @user1.password + click_on "Log in" + + Friendship.create(user: @user1, friend: @friend, confirmed: true) + + click_on "Mailbox" + + expect(page).not_to have_content "13987979245948945, 6597549169661591, + 19501227974711707, 15878093936820780, 17120061003915500, 1674708906257554, + 19207038272600258, 5842957003381056, 9179485880262553, 19536289285048631, + 2786926719715928, 11025250514274376, 17197200100095568, 29250927161084515, + 13819283828387603, 15802654134488562, 21826030828151967, + 25410511871589372" + end + + scenario "User cannot view unconfirmed friend's encrypted message in feed" do + @user1 = FactoryGirl.create(:user) + + visit root_path + + click_on "Sign In" + + fill_in "Login", with: @user1.email + fill_in "Password", with: @user1.password + click_on "Log in" + + Friendship.create(user: @user1, friend: @friend, confirmed: false) + + click_on "Feed" + + expect(page).not_to have_content "13987979245948945, 6597549169661591, + 19501227974711707, 15878093936820780, + 17120061003915500, 1674708906257554, + 19207038272600258, 5842957003381056, + 9179485880262553, 19536289285048631, + 2786926719715928, 11025250514274376, + 17197200100095568, 29250927161084515, + 13819283828387603, 15802654134488562, + 21826030828151967, 25410511871589372" + expect(page).not_to have_content @user.username + end + + scenario "User cannot view non-friend's encrypted message" do + @user1 = FactoryGirl.create(:user) + + visit root_path + + click_on "Sign In" + + fill_in "Login", with: @user1.email + fill_in "Password", with: @user1.password + click_on "Log in" + + click_on "Feed" + + expect(page).not_to have_content "13987979245948945, 6597549169661591, + 19501227974711707, 15878093936820780, + 17120061003915500, 1674708906257554, + 19207038272600258, 5842957003381056, + 9179485880262553, 19536289285048631, + 2786926719715928, 11025250514274376, + 17197200100095568, 29250927161084515, + 13819283828387603, 15802654134488562, + 21826030828151967, 25410511871589372" + + expect(page).not_to have_content @user.username + end +end diff --git a/spec/features/post/user_views_all_posts_spec.rb b/spec/features/post/user_views_all_posts_spec.rb index 784f8d1..8e6ffd8 100644 --- a/spec/features/post/user_views_all_posts_spec.rb +++ b/spec/features/post/user_views_all_posts_spec.rb @@ -2,12 +2,13 @@ feature "User views all posts", %{ As a user - I want to view messages + I want to view posts So that I can find out what my friends are saying } do # Acceptance Criteria # [x] I must be logged in to view posts + # [x] I can only see my friends' and my own posts # [x] I can see the username of the post creator # [x] I can see the date of creation # [x] I can see the time of creation @@ -27,9 +28,79 @@ click_on "Log in" end + scenario "User sees her inverse-friend's post" do + user = FactoryGirl.create(:user) + Friendship.create(user: user, friend: @user1, confirmed: true) + + post = FactoryGirl.create(:post, user: user) + + visit posts_path + + expect(page).to have_content post.body + + end + + scenario "User sees her friend's post" do + user = FactoryGirl.create(:user) + Friendship.create(user: @user1, friend: user, confirmed: true) + + post = FactoryGirl.create(:post, user: user) + + visit posts_path + + expect(page).to have_content post.body + end + + scenario "User sees her own post" do + post = FactoryGirl.create(:post, user: @user1) + + visit posts_path + + expect(page).to have_content post.body + end + + scenario "User does not see the post of a non-friend" do + post = FactoryGirl.create(:post) + + visit posts_path + + expect(page).not_to have_content post.body + + end + scenario "User does not see the post of a pending friend" do + user = FactoryGirl.create(:user) + Friendship.create(user: user, friend: @user1, confirmed: false) + + post = FactoryGirl.create(:post, user: user) + + visit posts_path + + expect(page).not_to have_content post.body + end + + scenario "User does not see the post of a requested friend" do + user = FactoryGirl.create(:user) + Friendship.create(user: @user1, friend: user, confirmed: false) + + post = FactoryGirl.create(:post, user: user) + + visit posts_path + + expect(page).not_to have_content post.body + end + scenario "User views multiple posts" do - post1 = FactoryGirl.create(:post, created_at: "2015-01-13 16:29:07 -0500") - post2 = FactoryGirl.create(:post) + + users = FactoryGirl.create_list(:user, 2) + Friendship.create(user: @user1, friend: users[0], confirmed: true) + Friendship.create(user: users[1], friend: @user1, confirmed: true) + + post1 = FactoryGirl.create( + :post, + user: users[0], + created_at: "2015-01-13 16:29:07 -0500" + ) + post2 = FactoryGirl.create(:post, user: users[1]) post3 = FactoryGirl.create(:post, user: @user1) visit posts_path diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index d3ef836..4529b76 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -14,4 +14,6 @@ this post is going to be longer than 255 characters" ) end + + it { should belong_to :user } end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 65caee0..200688c 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -8,4 +8,8 @@ it { should_not have_valid(:first_name).when(*blank_values) } it { should have_valid(:last_name).when("Any 1 .!@#$%^&*}text") } it { should_not have_valid(:last_name).when(*blank_values) } + + it { should have_many(:friendships).dependent(:destroy) } + it { should have_many(:inverse_friendships).dependent(:destroy) } + it { should have_many(:posts).dependent(:destroy) } end