unsakini 0.0.4.2 → 0.0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +26 -0
- data/angular/angular-cli.json +5 -3
- data/angular/npm-debug.log +54 -0
- data/angular/package.json +4 -1
- data/angular/src/app/app.component.html +1 -4
- data/angular/src/app/app.module.ts +8 -7
- data/angular/src/app/app.routes.module.ts +12 -2
- data/angular/src/app/confirm-account/confirm-account.component.ts +27 -0
- data/angular/src/app/confirm-account/confirm-account.html +41 -0
- data/angular/src/app/confirm-account/confirm-account.module.ts +24 -0
- data/angular/src/app/confirm-account/confirm-account.scss +3 -0
- data/angular/src/app/confirm-account/confirm-account.service.ts +27 -0
- data/angular/src/app/confirm-account/index.ts +3 -0
- data/angular/src/app/index.ts +2 -0
- data/angular/src/app/login/index.ts +3 -0
- data/angular/src/app/login/login.component.ts +40 -0
- data/angular/src/app/login/login.html +43 -0
- data/angular/src/app/login/login.module.ts +27 -0
- data/angular/src/app/login/login.service.ts +48 -0
- data/angular/src/app/registration/index.ts +3 -0
- data/angular/src/app/registration/registration.component.html +70 -12
- data/angular/src/app/registration/registration.component.spec.ts +8 -11
- data/angular/src/app/registration/registration.component.ts +10 -8
- data/angular/src/app/registration/registration.module.ts +23 -0
- data/angular/src/app/registration/registration.service.ts +46 -0
- data/angular/src/app/registration/registration.services.spec.ts +71 -0
- data/angular/src/app/services/auth-http/auth.http.service.ts +35 -0
- data/angular/src/app/services/auth-http/index.ts +1 -0
- data/angular/src/app/services/http/http.service.spec.ts +205 -0
- data/angular/src/app/services/http/http.service.ts +40 -0
- data/angular/src/app/services/http/index.ts +1 -0
- data/angular/src/app/services/index.ts +3 -0
- data/angular/src/app/services/services.module.ts +33 -0
- data/angular/src/assets/global.scss +3 -0
- data/angular/src/environments/custom.ts +4 -0
- data/angular/src/environments/environment.prod.ts +2 -1
- data/angular/src/environments/environment.ts +2 -1
- data/angular/src/index.html +1 -1
- data/app/controllers/application_controller.rb +2 -2
- data/app/controllers/concerns/unsakini/board_owner_controller_concern.rb +42 -0
- data/app/controllers/concerns/unsakini/comment_owner_controller_concern.rb +36 -0
- data/app/controllers/concerns/unsakini/logged_in_controller_concern.rb +23 -0
- data/app/controllers/concerns/unsakini/post_owner_controller_concern.rb +38 -0
- data/app/controllers/concerns/unsakini/serializer_controller_concern.rb +13 -0
- data/app/controllers/unsakini/base_controller.rb +6 -0
- data/app/controllers/unsakini/boards_controller.rb +76 -0
- data/app/controllers/unsakini/comments_controller.rb +54 -0
- data/app/controllers/unsakini/posts_controller.rb +61 -0
- data/app/controllers/unsakini/share_board_controller.rb +122 -0
- data/app/controllers/unsakini/user_token_controller.rb +17 -0
- data/app/controllers/unsakini/users_controller.rb +69 -0
- data/app/controllers/unsakini/web_controller.rb +27 -0
- data/app/mailers/unsakini/user_mailer.rb +13 -0
- data/app/models/concerns/unsakini/encryptable_model_concern.rb +97 -0
- data/app/models/unsakini/application_record.rb +7 -0
- data/app/models/unsakini/board.rb +16 -0
- data/app/models/unsakini/comment.rb +12 -0
- data/app/models/unsakini/post.rb +15 -0
- data/app/models/unsakini/user.rb +43 -0
- data/app/models/unsakini/user_board.rb +84 -0
- data/app/models/unsakini.rb +5 -0
- data/app/serializers/unsakini/board_serializer.rb +7 -0
- data/app/serializers/{comment_serializer.rb → unsakini/comment_serializer.rb} +6 -3
- data/app/serializers/unsakini/post_serializer.rb +26 -0
- data/app/serializers/unsakini/user_board_serializer.rb +14 -0
- data/app/serializers/{user_serializer.rb → unsakini/user_serializer.rb} +5 -2
- data/app/views/unsakini/user_mailer/confirm_account.html.erb +3 -0
- data/app/views/{web_base → unsakini/web}/index.html.erb +9 -7
- data/config/routes.rb +10 -10
- data/db/migrate/20161116114222_create_unsakini_boards.rb +10 -0
- data/db/migrate/{20161116200034_create_user_boards.rb → 20161116200034_create_unsakini_user_boards.rb} +3 -2
- data/db/migrate/{20161118031023_create_posts.rb → 20161118031023_create_unsakini_posts.rb} +2 -2
- data/db/migrate/{20161118100454_create_comments.rb → 20161118100454_create_unsakini_comments.rb} +2 -2
- data/db/migrate/20161126145352_create_unsakini_users.rb +15 -0
- data/lib/generators/unsakini/config/config_generator.rb +3 -1
- data/lib/generators/unsakini/dependencies/USAGE +5 -0
- data/lib/generators/unsakini/dependencies/dependencies_generator.rb +19 -0
- data/lib/tasks/unsakini_tasks.rake +6 -37
- data/lib/unsakini/engine.rb +2 -0
- data/lib/unsakini/version.rb +1 -1
- data/public/images/unsakini.svg +56 -0
- data/public/unsakini/app/448c34a56d699c29117adc64c43affeb.woff2 +0 -0
- data/public/unsakini/app/89889688147bd7575d6327160d64e760.svg +288 -0
- data/public/unsakini/app/assets/global.scss +3 -0
- data/public/unsakini/app/e18bbf611f2a2e43afc071aa2f4e1512.ttf +0 -0
- data/public/unsakini/app/f4769f9bdb7466be65088239c12046d1.eot +0 -0
- data/public/unsakini/app/fa2772327f55d8198301fdb8bcfc8158.woff +0 -0
- data/{angular/dist → public/unsakini/app}/favicon.ico +0 -0
- data/public/unsakini/app/index.html +14 -0
- data/public/unsakini/app/inline.d41d8cd98f00b204e980.bundle.js +2 -0
- data/public/unsakini/app/inline.d41d8cd98f00b204e980.bundle.map +1 -0
- data/public/unsakini/app/main.54f49c65d3d20650a5d5.bundle.js +2152 -0
- data/public/unsakini/app/main.54f49c65d3d20650a5d5.bundle.js.gz +0 -0
- data/public/unsakini/app/main.54f49c65d3d20650a5d5.bundle.map +1 -0
- data/public/unsakini/app/styles.58e065928ed8ebd0b582.bundle.js +2 -0
- data/public/unsakini/app/styles.58e065928ed8ebd0b582.bundle.map +1 -0
- data/public/unsakini/app/styles.5dac0e986fce6f8738b300cb558b56a0.bundle.css +8 -0
- data/spec/concerns/models/encryptable_concern.rb +3 -2
- data/spec/controllers/{web_base_controller_spec.rb → web_controller_spec.rb} +4 -3
- data/spec/dummy/config/application.rb +3 -1
- data/spec/dummy/config/environments/development.rb +2 -0
- data/spec/dummy/config/initializers/knock.rb +59 -0
- data/spec/dummy/db/schema.rb +16 -14
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/factories/boards.rb +1 -1
- data/spec/factories/comments.rb +1 -1
- data/spec/factories/posts.rb +1 -1
- data/spec/factories/user_boards.rb +1 -1
- data/spec/factories/users.rb +1 -1
- data/spec/models/board_spec.rb +2 -2
- data/spec/models/comment_spec.rb +2 -2
- data/spec/models/post_spec.rb +2 -2
- data/spec/models/user_board_spec.rb +19 -19
- data/spec/models/user_spec.rb +1 -1
- data/spec/requests/{api/boards/api_boards_crud_spec.rb → boards/boards_crud_spec.rb} +26 -26
- data/spec/requests/{api/boards/api_boards_pagination_spec.rb → boards/boards_pagination_spec.rb} +7 -7
- data/spec/requests/{api/boards/api_private_board_spec.rb → boards/private_board_spec.rb} +26 -26
- data/spec/requests/{api/boards/api_shared_board_spec.rb → boards/shared_board_spec.rb} +9 -9
- data/spec/requests/{api/boards/api_sharing_board_spec.rb → boards/sharing_board_spec.rb} +13 -13
- data/spec/requests/{api/comments/api_comments_pagination_spec.rb → comments/comments_pagination_spec.rb} +3 -3
- data/spec/requests/{api/comments/api_comments_private_board_spec.rb → comments/comments_private_board_spec.rb} +20 -20
- data/spec/requests/{api/comments/api_comments_shared_board_spec.rb → comments/comments_shared_board_spec.rb} +17 -17
- data/spec/requests/{api/posts/api_posts_pagination_spec.rb → posts/posts_pagination_spec.rb} +3 -3
- data/spec/requests/{api/posts/api_posts_private_board_spec.rb → posts/posts_private_board_spec.rb} +22 -22
- data/spec/requests/{api/posts/api_posts_shared_board_spec.rb → posts/posts_shared_board_spec.rb} +24 -24
- data/spec/requests/{api/user/api_user_create_spec.rb → user/user_create_spec.rb} +19 -23
- data/spec/requests/{api/user/api_user_search_spec.rb → user/user_search_spec.rb} +9 -9
- data/spec/schema/jwt.json +9 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/auth_helper.rb +0 -2
- metadata +133 -200
- data/angular/dist/index.html +0 -14
- data/angular/dist/inline.bundle.js +0 -139
- data/angular/dist/inline.map +0 -1
- data/angular/dist/main.bundle.js +0 -64689
- data/angular/dist/main.map +0 -1
- data/angular/dist/styles.bundle.js +0 -364
- data/angular/dist/styles.map +0 -1
- data/angular/src/styles.css +0 -1
- data/app/controllers/api/boards_controller.rb +0 -73
- data/app/controllers/api/comments_controller.rb +0 -51
- data/app/controllers/api/posts_controller.rb +0 -58
- data/app/controllers/api/share_board_controller.rb +0 -118
- data/app/controllers/api/users_controller.rb +0 -40
- data/app/controllers/concerns/board_owner_controller_concern.rb +0 -38
- data/app/controllers/concerns/comment_owner_controller_concern.rb +0 -33
- data/app/controllers/concerns/logged_in_controller_concern.rb +0 -21
- data/app/controllers/concerns/post_owner_controller_concern.rb +0 -36
- data/app/controllers/concerns/serializer_controller_concern.rb +0 -11
- data/app/controllers/user_token_controller.rb +0 -2
- data/app/controllers/web_base_controller.rb +0 -23
- data/app/models/application_record.rb +0 -5
- data/app/models/board.rb +0 -14
- data/app/models/comment.rb +0 -9
- data/app/models/concerns/encryptable_model_concern.rb +0 -96
- data/app/models/post.rb +0 -12
- data/app/models/user.rb +0 -15
- data/app/models/user_board.rb +0 -82
- data/app/serializers/board_serializer.rb +0 -5
- data/app/serializers/post_serializer.rb +0 -23
- data/app/serializers/user_board_serializer.rb +0 -11
- data/db/migrate/20161116114222_create_boards.rb +0 -9
- data/db/migrate/20161118221508_add_encrypted_password_to_user_board.rb +0 -5
- data/db/migrate/20161122211105_create_users.rb +0 -12
- data/db/migrate/20161124102633_add_is_shared_to_boards.rb +0 -5
- data/lib/generators/unsakini/angular/USAGE +0 -8
- data/lib/generators/unsakini/angular/angular_generator.rb +0 -7
- data/public/images/logo.svg +0 -619
- data/spec/dummy/config/initializers/assets.rb +0 -11
- data/spec/dummy/config/initializers/cookies_serializer.rb +0 -5
- data/spec/dummy/config/initializers/session_store.rb +0 -3
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20161124210219_create_boards.unsakini_engine.rb +0 -10
- data/spec/dummy/db/migrate/20161124210220_create_user_boards.unsakini_engine.rb +0 -12
- data/spec/dummy/db/migrate/20161124210221_create_posts.unsakini_engine.rb +0 -13
- data/spec/dummy/db/migrate/20161124210222_create_comments.unsakini_engine.rb +0 -12
- data/spec/dummy/db/migrate/20161124210223_add_encrypted_password_to_user_board.unsakini_engine.rb +0 -6
- data/spec/dummy/db/migrate/20161124210224_create_users.unsakini_engine.rb +0 -13
- data/spec/dummy/db/migrate/20161124210225_add_is_shared_to_boards.unsakini_engine.rb +0 -6
- data/spec/dummy/public/app/favicon.ico +0 -0
- data/spec/dummy/public/app/index.html +0 -14
- data/spec/dummy/public/app/inline.bundle.js +0 -139
- data/spec/dummy/public/app/inline.map +0 -1
- data/spec/dummy/public/app/main.bundle.js +0 -64689
- data/spec/dummy/public/app/main.map +0 -1
- data/spec/dummy/public/app/styles.bundle.js +0 -364
- data/spec/dummy/public/app/styles.map +0 -1
- data/spec/dummy/tmp/unsakini-ng2/LICENSE +0 -21
- data/spec/dummy/tmp/unsakini-ng2/README.md +0 -1
- data/spec/dummy/tmp/unsakini-ng2/angular-cli.json +0 -59
- data/spec/dummy/tmp/unsakini-ng2/e2e/app.e2e-spec.ts +0 -14
- data/spec/dummy/tmp/unsakini-ng2/e2e/app.po.ts +0 -11
- data/spec/dummy/tmp/unsakini-ng2/e2e/signup.e2e-spec.ts +0 -28
- data/spec/dummy/tmp/unsakini-ng2/e2e/signup.po.ts +0 -31
- data/spec/dummy/tmp/unsakini-ng2/e2e/tsconfig.json +0 -16
- data/spec/dummy/tmp/unsakini-ng2/karma.conf.js +0 -45
- data/spec/dummy/tmp/unsakini-ng2/package.json +0 -49
- data/spec/dummy/tmp/unsakini-ng2/protractor.conf.js +0 -32
- data/spec/dummy/tmp/unsakini-ng2/src/app/app.component.css +0 -0
- data/spec/dummy/tmp/unsakini-ng2/src/app/app.component.html +0 -4
- data/spec/dummy/tmp/unsakini-ng2/src/app/app.component.spec.ts +0 -47
- data/spec/dummy/tmp/unsakini-ng2/src/app/app.component.ts +0 -10
- data/spec/dummy/tmp/unsakini-ng2/src/app/app.module.ts +0 -29
- data/spec/dummy/tmp/unsakini-ng2/src/app/app.routes.module.ts +0 -29
- data/spec/dummy/tmp/unsakini-ng2/src/app/index.ts +0 -2
- data/spec/dummy/tmp/unsakini-ng2/src/app/registration/registration.component.css +0 -0
- data/spec/dummy/tmp/unsakini-ng2/src/app/registration/registration.component.html +0 -14
- data/spec/dummy/tmp/unsakini-ng2/src/app/registration/registration.component.spec.ts +0 -157
- data/spec/dummy/tmp/unsakini-ng2/src/app/registration/registration.component.ts +0 -42
- data/spec/dummy/tmp/unsakini-ng2/src/environments/environment.prod.ts +0 -3
- data/spec/dummy/tmp/unsakini-ng2/src/environments/environment.ts +0 -8
- data/spec/dummy/tmp/unsakini-ng2/src/favicon.ico +0 -0
- data/spec/dummy/tmp/unsakini-ng2/src/index.html +0 -14
- data/spec/dummy/tmp/unsakini-ng2/src/main.ts +0 -12
- data/spec/dummy/tmp/unsakini-ng2/src/polyfills.ts +0 -19
- data/spec/dummy/tmp/unsakini-ng2/src/styles.css +0 -1
- data/spec/dummy/tmp/unsakini-ng2/src/test.ts +0 -31
- data/spec/dummy/tmp/unsakini-ng2/src/tsconfig.json +0 -18
- data/spec/dummy/tmp/unsakini-ng2/src/typings.d.ts +0 -2
- data/spec/dummy/tmp/unsakini-ng2/tslint.json +0 -114
- data/spec/dummy/tmp/unsakini-ng2/typings.json +0 -4
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Ensures users are logged in and sets `@user` instance variable in the controllers.
|
|
2
|
+
# This is included in the base api controller.
|
|
3
|
+
#
|
|
4
|
+
# Returns `401` error if user is not authenticated
|
|
5
|
+
module Unsakini
|
|
6
|
+
module LoggedInControllerConcern
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
include Knock::Authenticable
|
|
11
|
+
before_action :ensure_logged_in
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def ensure_logged_in
|
|
17
|
+
authenticate_for Unsakini::User
|
|
18
|
+
@user = current_unsakini_user
|
|
19
|
+
head :unauthorized unless @user
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Ensures user is owner of the post and sets the `@post` variable in the controllers
|
|
2
|
+
module Unsakini
|
|
3
|
+
module PostOwnerControllerConcern
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
# Ensures user is owner of the post and sets the `@post` variable in the controllers
|
|
7
|
+
def ensure_post
|
|
8
|
+
post_id = params[:post_id] || params[:id]
|
|
9
|
+
board_id = params[:board_id]
|
|
10
|
+
result = has_post_access(board_id, post_id)
|
|
11
|
+
status = result[:status]
|
|
12
|
+
@post = result[:post]
|
|
13
|
+
head status if status != :ok
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Validate if user has access to the post in the board
|
|
17
|
+
#
|
|
18
|
+
# @param board_id [Integer] board id
|
|
19
|
+
# @param post_id [Integer] post id
|
|
20
|
+
def has_post_access(board_id, post_id)
|
|
21
|
+
post = Unsakini::Post.where(id: post_id, board_id: board_id)
|
|
22
|
+
.joins("LEFT JOIN #{UserBoard.table_name} ON #{UserBoard.table_name}.board_id = #{Post.table_name}.board_id")
|
|
23
|
+
.where("#{UserBoard.table_name}.user_id = ?", @user.id)
|
|
24
|
+
.first
|
|
25
|
+
if post.nil?
|
|
26
|
+
return {status: :forbidden}
|
|
27
|
+
else
|
|
28
|
+
return {status: :ok, post: post}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Ensures user is owner of the post. Must be run after {#ensure_post}`.
|
|
33
|
+
def ensure_post_owner
|
|
34
|
+
render json: {}, status: :forbidden if @post.user_id != @user.id
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
|
|
2
|
+
module Unsakini
|
|
3
|
+
class BoardsController < BaseController
|
|
4
|
+
|
|
5
|
+
include LoggedInControllerConcern
|
|
6
|
+
include SerializerControllerConcern
|
|
7
|
+
include BoardOwnerControllerConcern
|
|
8
|
+
include ::ActionController::Serialization
|
|
9
|
+
|
|
10
|
+
before_action :ensure_board, :only => [:show, :update, :destroy]
|
|
11
|
+
before_action :ensure_board_owner, :only => [:update, :destroy]
|
|
12
|
+
|
|
13
|
+
# Returns boards belonging to current user
|
|
14
|
+
#
|
|
15
|
+
# `GET /api/boards`
|
|
16
|
+
#
|
|
17
|
+
def index
|
|
18
|
+
admin = true
|
|
19
|
+
shared = false
|
|
20
|
+
admin = params[:is_admin] == 'true' if params[:admin]
|
|
21
|
+
shared = params[:shared] == 'true' if params[:shared]
|
|
22
|
+
result = @user.user_boards.shared(shared)
|
|
23
|
+
result = result.admin if admin
|
|
24
|
+
paginate json: result, per_page: 10
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Creates board belonging to current user.
|
|
28
|
+
#
|
|
29
|
+
# `POST /api/boards`
|
|
30
|
+
#
|
|
31
|
+
def create
|
|
32
|
+
@user_board = UserBoard.new(
|
|
33
|
+
name: params[:board][:name],
|
|
34
|
+
user_id: @user.id,
|
|
35
|
+
encrypted_password: params[:encrypted_password],
|
|
36
|
+
is_admin: true
|
|
37
|
+
)
|
|
38
|
+
if @user_board.save
|
|
39
|
+
render json: @user_board, status: :created
|
|
40
|
+
else
|
|
41
|
+
render json: @user_board.errors.full_messages, status: 422
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Render a single board.
|
|
47
|
+
#
|
|
48
|
+
# `GET /api/boards/:id`
|
|
49
|
+
#
|
|
50
|
+
def show
|
|
51
|
+
render json: @user_board
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Updates a single board.
|
|
55
|
+
#
|
|
56
|
+
# `PUT /api/boards/:id`
|
|
57
|
+
def update
|
|
58
|
+
if @user_board.update(name: params[:board][:name], encrypted_password: params[:encrypted_password])
|
|
59
|
+
render json: @user_board
|
|
60
|
+
else
|
|
61
|
+
errors = @board.errors.full_messages.concat @user_board.errors.full_messages
|
|
62
|
+
render json: errors, status: 422
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Deletes a board resource.
|
|
67
|
+
#
|
|
68
|
+
# `DELETE /api/boards/:id`
|
|
69
|
+
def destroy
|
|
70
|
+
@board.destroy
|
|
71
|
+
render json: {}, status: :ok
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Unsakini
|
|
2
|
+
class CommentsController < BaseController
|
|
3
|
+
|
|
4
|
+
include LoggedInControllerConcern
|
|
5
|
+
include PostOwnerControllerConcern
|
|
6
|
+
include CommentOwnerControllerConcern
|
|
7
|
+
include ::ActionController::Serialization
|
|
8
|
+
|
|
9
|
+
before_action :ensure_post, only: [:index, :create]
|
|
10
|
+
before_action :ensure_comment, only: [:show, :update, :destroy]
|
|
11
|
+
before_action :ensure_comment_owner, only: [:update, :destroy]
|
|
12
|
+
|
|
13
|
+
# Renders the comments belonging to the post
|
|
14
|
+
#
|
|
15
|
+
# `GET /api/boards/:board_id/posts/:post_id/`
|
|
16
|
+
def index
|
|
17
|
+
paginate json: @post.comments.page(params[:page]), per_page: 20
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Creates new comment belonging to the post
|
|
21
|
+
#
|
|
22
|
+
# `POST /api/boards/:board_id/posts/:post_id/`
|
|
23
|
+
def create
|
|
24
|
+
@comment = Comment.new(params.permit(:content))
|
|
25
|
+
@comment.user = @user
|
|
26
|
+
@comment.post = @post
|
|
27
|
+
if @comment.save
|
|
28
|
+
render json: @comment
|
|
29
|
+
else
|
|
30
|
+
render json: @comment.errors, status: 422
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Updates a comment
|
|
35
|
+
#
|
|
36
|
+
# `PUT /api/boards/:board_id/posts/:post_id/comments/:id`
|
|
37
|
+
def update
|
|
38
|
+
if @comment.update(params.permit(:content))
|
|
39
|
+
render json: @comment
|
|
40
|
+
else
|
|
41
|
+
render json: @comment.errors, status: 422
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Deletes a comment
|
|
46
|
+
#
|
|
47
|
+
# `DELETE /api/boards/:board_id/posts/:post_id/comments/:id`
|
|
48
|
+
def destroy
|
|
49
|
+
@comment.destroy
|
|
50
|
+
render json: {}, status: :ok
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Unsakini
|
|
2
|
+
class PostsController < BaseController
|
|
3
|
+
|
|
4
|
+
include LoggedInControllerConcern
|
|
5
|
+
include BoardOwnerControllerConcern
|
|
6
|
+
include PostOwnerControllerConcern
|
|
7
|
+
include ::ActionController::Serialization
|
|
8
|
+
|
|
9
|
+
before_action :ensure_board, only: [:index, :create]
|
|
10
|
+
before_action :ensure_post, only: [:show, :update, :destroy]
|
|
11
|
+
before_action :ensure_post_owner, only: [:update, :destroy]
|
|
12
|
+
|
|
13
|
+
# Renders the post belonging to the board
|
|
14
|
+
#
|
|
15
|
+
# `GET /api/boards/:board_id/posts`
|
|
16
|
+
def index
|
|
17
|
+
paginate json: @board.posts.page(params[:page]), per_page: 20
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Renders a single post belonging to the board
|
|
21
|
+
#
|
|
22
|
+
# `GET /api/boards/:board_id/posts/:id`
|
|
23
|
+
def show
|
|
24
|
+
render json: @post
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Creates a single post belonging to the board
|
|
28
|
+
#
|
|
29
|
+
# `POST /api/boards/:board_id/posts/`
|
|
30
|
+
def create
|
|
31
|
+
@post = Post.new(params.permit(:title, :content, :board_id))
|
|
32
|
+
@post.user = @user
|
|
33
|
+
if (@post.save)
|
|
34
|
+
render json: @post, status: :created
|
|
35
|
+
else
|
|
36
|
+
render json: @post.errors, status: 422
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Updates a single post belonging to the board
|
|
41
|
+
#
|
|
42
|
+
# `PUT /api/boards/:board_id/posts/:id`
|
|
43
|
+
def update
|
|
44
|
+
if (@post.update(params.permit(:title, :content)))
|
|
45
|
+
render json: @post, status: :ok
|
|
46
|
+
else
|
|
47
|
+
render json: @post.errors, status: 422
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Deletes a single post belonging to the board
|
|
52
|
+
#
|
|
53
|
+
# `DELETE /api/boards/:board_id/posts/:id`
|
|
54
|
+
def destroy
|
|
55
|
+
@post.destroy
|
|
56
|
+
render json: {}, status: :ok
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
module Unsakini
|
|
2
|
+
|
|
3
|
+
class ShareBoardController < BaseController
|
|
4
|
+
|
|
5
|
+
include LoggedInControllerConcern
|
|
6
|
+
include BoardOwnerControllerConcern
|
|
7
|
+
include PostOwnerControllerConcern
|
|
8
|
+
include CommentOwnerControllerConcern
|
|
9
|
+
|
|
10
|
+
before_action :validate_params
|
|
11
|
+
|
|
12
|
+
# Shares a board to other users. Example payload param:
|
|
13
|
+
#
|
|
14
|
+
# `POST /api/share/board`
|
|
15
|
+
#
|
|
16
|
+
# ```
|
|
17
|
+
# {
|
|
18
|
+
# board: {
|
|
19
|
+
# id: 1,
|
|
20
|
+
# name: 'some encrypted text',
|
|
21
|
+
# },
|
|
22
|
+
# posts: [
|
|
23
|
+
# {
|
|
24
|
+
# board_id: 1,
|
|
25
|
+
# title: 'some encrypted text',
|
|
26
|
+
# content: 'some encrypted text',
|
|
27
|
+
# comments: [
|
|
28
|
+
# {
|
|
29
|
+
# id: 1,
|
|
30
|
+
# content: 'some encrypted text',
|
|
31
|
+
# user_id: 1,
|
|
32
|
+
# post_id: 1,
|
|
33
|
+
# }
|
|
34
|
+
# ]
|
|
35
|
+
# }
|
|
36
|
+
# ],
|
|
37
|
+
# shared_user_ids: [1, 2, 3, 4],
|
|
38
|
+
# encrypted_password: 'some encrypted password'
|
|
39
|
+
# }
|
|
40
|
+
# ```
|
|
41
|
+
# The `encrypted_password` param will be used to decrypt contents of this board. The encryption happens in the client so
|
|
42
|
+
# the server don't really know what is the original password. The board creator will have to share it privately to other users whom he/she shared it with so they can access the board.
|
|
43
|
+
#
|
|
44
|
+
# `posts` and `comments` fields can be empty.
|
|
45
|
+
def index
|
|
46
|
+
ActiveRecord::Base.transaction do
|
|
47
|
+
if params[:posts]
|
|
48
|
+
params[:posts].each do |post|
|
|
49
|
+
p = Post.find(post[:id])
|
|
50
|
+
p.title = post[:title]
|
|
51
|
+
p.content = post[:content]
|
|
52
|
+
p.save!
|
|
53
|
+
|
|
54
|
+
if post[:comments] and p.valid?
|
|
55
|
+
post[:comments].each do |comment|
|
|
56
|
+
c = Comment.find(comment[:id])
|
|
57
|
+
c.content = comment[:content]
|
|
58
|
+
c.save!
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
if @user_board.share(params[:shared_user_ids], params[:encrypted_password])
|
|
64
|
+
render json: {}, status: :ok
|
|
65
|
+
else
|
|
66
|
+
raise "An error occured"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
rescue
|
|
70
|
+
# clean up the created {UserBoard}s
|
|
71
|
+
render json: ["Some of the data can't be saved."], status: 422
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Validates the contents of params against the database records.
|
|
75
|
+
def validate_params
|
|
76
|
+
|
|
77
|
+
if params[:encrypted_password].nil? or params[:shared_user_ids].nil? or params[:board].nil?
|
|
78
|
+
render json: {}, status: 422
|
|
79
|
+
return
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
result = has_board_access(params[:board][:id])
|
|
83
|
+
if result[:status] != :ok
|
|
84
|
+
render json: {}, status: result[:status]
|
|
85
|
+
return
|
|
86
|
+
else
|
|
87
|
+
if !result[:user_board].is_admin
|
|
88
|
+
render json: {}, status: :forbidden
|
|
89
|
+
return
|
|
90
|
+
end
|
|
91
|
+
@board = result[:board]
|
|
92
|
+
@user_board = result[:user_board]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if params[:posts]
|
|
96
|
+
|
|
97
|
+
params[:posts].each do |post|
|
|
98
|
+
s = has_post_access(params[:board][:id], post[:id])[:status]
|
|
99
|
+
if s != :ok
|
|
100
|
+
render json: {}, status: s
|
|
101
|
+
return
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
if post[:comments]
|
|
105
|
+
post[:comments].each do |comment|
|
|
106
|
+
s = has_comment_access(post[:id], comment[:id])[:status]
|
|
107
|
+
if s != :ok
|
|
108
|
+
render json: {}, status: s
|
|
109
|
+
return
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Unsakini
|
|
2
|
+
class UserTokenController < Knock::AuthTokenController
|
|
3
|
+
def create
|
|
4
|
+
if entity.confirmed_at?
|
|
5
|
+
render json: auth_token, status: :created
|
|
6
|
+
else
|
|
7
|
+
res = {message: "Your account needs confirmation. Please follow the confirmation instructions sent to #{auth_params[:email]}"}
|
|
8
|
+
render status: 401, json: res
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def entity_name
|
|
13
|
+
self.class.name.split('TokenController').first
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module Unsakini
|
|
2
|
+
|
|
3
|
+
class UsersController < BaseController
|
|
4
|
+
|
|
5
|
+
include LoggedInControllerConcern
|
|
6
|
+
include ::ActionController::Serialization
|
|
7
|
+
|
|
8
|
+
skip_before_action :ensure_logged_in, only: [:create, :confirm]
|
|
9
|
+
|
|
10
|
+
#Creates a new user
|
|
11
|
+
def create
|
|
12
|
+
user = User.new(user_params)
|
|
13
|
+
|
|
14
|
+
if user.save
|
|
15
|
+
UserMailer.confirm_account(user).deliver_now
|
|
16
|
+
render json: user, status: :created
|
|
17
|
+
else
|
|
18
|
+
render json: user.errors, status: 422
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# confirm user account
|
|
23
|
+
def confirm
|
|
24
|
+
token = params[:token].to_s
|
|
25
|
+
|
|
26
|
+
user = User.find_by(confirmation_token: token)
|
|
27
|
+
|
|
28
|
+
if user.present? && user.confirmation_token_valid?
|
|
29
|
+
if user.mark_as_confirmed!
|
|
30
|
+
render json: {status: 'User confirmed successfully'}, status: :ok
|
|
31
|
+
else
|
|
32
|
+
render json: user.errors, status: 422
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
render json: ['Invalid token'], status: :not_found
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Renders the current user as json
|
|
40
|
+
#
|
|
41
|
+
# `GET /api/user/:id`
|
|
42
|
+
#
|
|
43
|
+
def show
|
|
44
|
+
render json: @user
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Returns the user with matching email
|
|
48
|
+
#
|
|
49
|
+
# `GET /api/users/search?email=xxx`
|
|
50
|
+
#
|
|
51
|
+
def search
|
|
52
|
+
user = User.where("email = ? AND id != ?", params[:email], @user.id).first
|
|
53
|
+
if user
|
|
54
|
+
render json: user
|
|
55
|
+
else
|
|
56
|
+
render json: {}, status: :not_found
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def user_params
|
|
63
|
+
params.permit(:name, :email, :password, :password_confirmation)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Base controller for web pages
|
|
2
|
+
|
|
3
|
+
module Unsakini
|
|
4
|
+
class WebController < ActionController::Base
|
|
5
|
+
include ActionController::ImplicitRender
|
|
6
|
+
include ActionView::Layouts
|
|
7
|
+
|
|
8
|
+
# Renders welcome page
|
|
9
|
+
def index
|
|
10
|
+
@project = 'Unsakini'
|
|
11
|
+
@description = 'Privacy. Confidentiality. Security.'
|
|
12
|
+
@version = VERSION
|
|
13
|
+
@author = 'Adones Pitogo'
|
|
14
|
+
@repository = 'https://github.com/adonespitogo/unsakini'
|
|
15
|
+
@title = "#{@project} | #{@description}"
|
|
16
|
+
@tagline = "Created by and for online activists, information security enthusiasts and government surveillance evaders."
|
|
17
|
+
@keywords = "unsakini, encrypted, bulletin board, BB, ruby, rails"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Renders the angular index view when request url is /app/* to enable html5 pushState capability of angularjs
|
|
21
|
+
def app
|
|
22
|
+
gem_root = File.expand_path '../../../..', __FILE__
|
|
23
|
+
|
|
24
|
+
render file: "#{gem_root}/public/unsakini/app/index.html", layout: false
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Unsakini
|
|
2
|
+
class UserMailer < ::ActionMailer::Base
|
|
3
|
+
default from: 'notifications@example.com'
|
|
4
|
+
|
|
5
|
+
def confirm_account(user)
|
|
6
|
+
@user = user
|
|
7
|
+
@url = "#{root_url}app/account/confirm/#{@user.confirmation_token}"
|
|
8
|
+
mail(to: @user.email, subject: 'Unsakini - Account Confirmation')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'base64'
|
|
3
|
+
|
|
4
|
+
# Responsible for encryption and decryption of certain model attributes
|
|
5
|
+
module Unsakini
|
|
6
|
+
module EncryptableModelConcern
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
before_save :encrypt_encryptable_attributes
|
|
11
|
+
after_save :decrypt_encryptable_attributes
|
|
12
|
+
after_find :decrypt_encryptable_attributes
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module ClassMethods
|
|
16
|
+
# Sets the `encryptable_attributes` class instance variable in the model.
|
|
17
|
+
#
|
|
18
|
+
# Encryptable attributes are encrypted before saving using `before_save` hook and decrypted using `after_save` and `after_find` hooks.
|
|
19
|
+
#
|
|
20
|
+
# Example:
|
|
21
|
+
# ```
|
|
22
|
+
# class Board < BaseModel
|
|
23
|
+
# encryptable_attributes :name, :title, :content
|
|
24
|
+
# end
|
|
25
|
+
# ```
|
|
26
|
+
# @param attrs [Symbol] model attributes
|
|
27
|
+
#
|
|
28
|
+
def encryptable_attributes(*attrs)
|
|
29
|
+
@encryptable_attributes = attrs
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns the model's `encryptable_attributes` class instance variable.
|
|
35
|
+
#
|
|
36
|
+
def encryptable_attributes
|
|
37
|
+
self.class.instance_variable_get(:@encryptable_attributes) || []
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
# Encryptes the model's encryptable attributes before saving using Rails' `before_save` hook.
|
|
42
|
+
#
|
|
43
|
+
# **Note: Be careful in calling this method manually as it can corrupt the data.**
|
|
44
|
+
def encrypt_encryptable_attributes
|
|
45
|
+
encryptable_attributes.each do |k|
|
|
46
|
+
self[k] = encrypt(self[k])
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Decrypts the model's encryptable attributes using Rails' `after_save` and `after_find` hooks.
|
|
51
|
+
#
|
|
52
|
+
# **Note: Be careful in calling this method manually as it can corrupt the data.**
|
|
53
|
+
def decrypt_encryptable_attributes
|
|
54
|
+
encryptable_attributes.each do |k|
|
|
55
|
+
self[k] = decrypt(self[k])
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Determins if the value being encrypted/decryped is empty.
|
|
60
|
+
def is_empty_val(value)
|
|
61
|
+
!value or value.nil? or value == ""
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Returns the cipher algorithm used
|
|
65
|
+
def cipher
|
|
66
|
+
OpenSSL::Cipher::Cipher.new('aes-256-cbc')
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Returns the encryption key from the `unsakini_crypto_key` config
|
|
70
|
+
def cipher_key
|
|
71
|
+
begin
|
|
72
|
+
Rails.configuration.unsakini_crypto_key
|
|
73
|
+
rescue Exception => e
|
|
74
|
+
raise 'Encryption key is not set! Please run `rails g unsakini:config` before you proceed.'
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Encrypts model attribute value
|
|
79
|
+
def encrypt(value)
|
|
80
|
+
return value if is_empty_val(value)
|
|
81
|
+
c = cipher.encrypt
|
|
82
|
+
c.key = Digest::SHA256.digest(cipher_key)
|
|
83
|
+
c.iv = iv = c.random_iv
|
|
84
|
+
Base64.encode64(iv) + Base64.encode64(c.update(value.to_s) + c.final)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Decrypts model attribute value
|
|
88
|
+
def decrypt(value)
|
|
89
|
+
return value if is_empty_val(value)
|
|
90
|
+
c = cipher.decrypt
|
|
91
|
+
c.key = Digest::SHA256.digest(cipher_key)
|
|
92
|
+
c.iv = Base64.decode64 value.slice!(0,25)
|
|
93
|
+
c.update(Base64.decode64(value.to_s)) + c.final
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#Board model
|
|
2
|
+
module Unsakini
|
|
3
|
+
class Board < ApplicationRecord
|
|
4
|
+
|
|
5
|
+
include EncryptableModelConcern
|
|
6
|
+
encryptable_attributes :name
|
|
7
|
+
|
|
8
|
+
validates :name, presence: true
|
|
9
|
+
|
|
10
|
+
has_many :users, through: :user_boards
|
|
11
|
+
|
|
12
|
+
has_many :user_boards, :dependent => :delete_all
|
|
13
|
+
has_many :posts, :dependent => :destroy
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#Post model
|
|
2
|
+
|
|
3
|
+
module Unsakini
|
|
4
|
+
class Post < ApplicationRecord
|
|
5
|
+
include EncryptableModelConcern
|
|
6
|
+
|
|
7
|
+
encryptable_attributes :title, :content
|
|
8
|
+
validates :title, presence: true
|
|
9
|
+
validates :content, presence: true
|
|
10
|
+
|
|
11
|
+
belongs_to :user
|
|
12
|
+
belongs_to :board
|
|
13
|
+
has_many :comments, :dependent => :delete_all
|
|
14
|
+
end
|
|
15
|
+
end
|