unsakini 0.0.4.pre.1 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (223) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -27
  3. data/angular/angular-cli.json +3 -5
  4. data/{public/unsakini/app → angular/dist}/favicon.ico +0 -0
  5. data/angular/dist/index.html +14 -0
  6. data/angular/dist/inline.bundle.js +139 -0
  7. data/angular/dist/inline.map +1 -0
  8. data/angular/dist/main.bundle.js +64689 -0
  9. data/angular/dist/main.map +1 -0
  10. data/angular/dist/styles.bundle.js +364 -0
  11. data/angular/dist/styles.map +1 -0
  12. data/angular/package.json +1 -4
  13. data/angular/src/app/app.component.html +4 -1
  14. data/angular/src/app/app.module.ts +7 -8
  15. data/angular/src/app/app.routes.module.ts +2 -12
  16. data/angular/src/app/index.ts +0 -2
  17. data/angular/src/app/registration/registration.component.html +12 -70
  18. data/angular/src/app/registration/registration.component.spec.ts +11 -8
  19. data/angular/src/app/registration/registration.component.ts +8 -10
  20. data/angular/src/environments/environment.prod.ts +1 -2
  21. data/angular/src/environments/environment.ts +1 -2
  22. data/angular/src/index.html +1 -1
  23. data/angular/src/styles.css +1 -0
  24. data/app/controllers/api/boards_controller.rb +73 -0
  25. data/app/controllers/api/comments_controller.rb +51 -0
  26. data/app/controllers/api/posts_controller.rb +58 -0
  27. data/app/controllers/api/share_board_controller.rb +118 -0
  28. data/app/controllers/api/users_controller.rb +40 -0
  29. data/app/controllers/application_controller.rb +2 -2
  30. data/app/controllers/concerns/board_owner_controller_concern.rb +38 -0
  31. data/app/controllers/concerns/comment_owner_controller_concern.rb +33 -0
  32. data/app/controllers/concerns/logged_in_controller_concern.rb +21 -0
  33. data/app/controllers/concerns/post_owner_controller_concern.rb +36 -0
  34. data/app/controllers/concerns/serializer_controller_concern.rb +11 -0
  35. data/app/controllers/user_token_controller.rb +2 -0
  36. data/app/controllers/web_base_controller.rb +23 -0
  37. data/app/models/application_record.rb +5 -0
  38. data/app/models/board.rb +14 -0
  39. data/app/models/comment.rb +9 -0
  40. data/app/models/concerns/encryptable_model_concern.rb +96 -0
  41. data/app/models/post.rb +12 -0
  42. data/app/models/user.rb +15 -0
  43. data/app/models/user_board.rb +82 -0
  44. data/app/serializers/board_serializer.rb +5 -0
  45. data/app/serializers/{unsakini/comment_serializer.rb → comment_serializer.rb} +3 -6
  46. data/app/serializers/post_serializer.rb +23 -0
  47. data/app/serializers/user_board_serializer.rb +11 -0
  48. data/app/serializers/{unsakini/user_serializer.rb → user_serializer.rb} +2 -5
  49. data/app/views/{unsakini/web → web_base}/index.html.erb +169 -65
  50. data/config/routes.rb +10 -10
  51. data/db/migrate/20161116114222_create_boards.rb +9 -0
  52. data/db/migrate/{20161116200034_create_unsakini_user_boards.rb → 20161116200034_create_user_boards.rb} +2 -3
  53. data/db/migrate/{20161118031023_create_unsakini_posts.rb → 20161118031023_create_posts.rb} +2 -2
  54. data/db/migrate/{20161118100454_create_unsakini_comments.rb → 20161118100454_create_comments.rb} +2 -2
  55. data/db/migrate/20161118221508_add_encrypted_password_to_user_board.rb +5 -0
  56. data/db/migrate/20161122211105_create_users.rb +12 -0
  57. data/db/migrate/20161124102633_add_is_shared_to_boards.rb +5 -0
  58. data/lib/generators/unsakini/angular/USAGE +8 -0
  59. data/lib/generators/unsakini/angular/angular_generator.rb +7 -0
  60. data/lib/generators/unsakini/config/config_generator.rb +1 -3
  61. data/lib/tasks/unsakini_tasks.rake +37 -6
  62. data/lib/unsakini/engine.rb +0 -2
  63. data/lib/unsakini/version.rb +1 -1
  64. data/public/css/custom.css +1 -5
  65. data/public/images/logo.svg +619 -0
  66. data/spec/concerns/models/encryptable_concern.rb +2 -3
  67. data/spec/controllers/{web_controller_spec.rb → web_base_controller_spec.rb} +3 -4
  68. data/spec/dummy/config/application.rb +1 -3
  69. data/spec/dummy/config/environments/development.rb +0 -2
  70. data/spec/dummy/config/initializers/assets.rb +11 -0
  71. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  72. data/spec/dummy/config/initializers/session_store.rb +3 -0
  73. data/spec/dummy/db/development.sqlite3 +0 -0
  74. data/spec/dummy/db/migrate/20161124210219_create_boards.unsakini_engine.rb +10 -0
  75. data/spec/dummy/db/migrate/20161124210220_create_user_boards.unsakini_engine.rb +12 -0
  76. data/spec/dummy/db/migrate/20161124210221_create_posts.unsakini_engine.rb +13 -0
  77. data/spec/dummy/db/migrate/20161124210222_create_comments.unsakini_engine.rb +12 -0
  78. data/spec/dummy/db/migrate/20161124210223_add_encrypted_password_to_user_board.unsakini_engine.rb +6 -0
  79. data/spec/dummy/db/migrate/20161124210224_create_users.unsakini_engine.rb +13 -0
  80. data/spec/dummy/db/migrate/20161124210225_add_is_shared_to_boards.unsakini_engine.rb +6 -0
  81. data/spec/dummy/db/schema.rb +14 -16
  82. data/spec/dummy/db/test.sqlite3 +0 -0
  83. data/spec/dummy/public/app/favicon.ico +0 -0
  84. data/spec/dummy/public/app/index.html +14 -0
  85. data/spec/dummy/public/app/inline.bundle.js +139 -0
  86. data/spec/dummy/public/app/inline.map +1 -0
  87. data/spec/dummy/public/app/main.bundle.js +64689 -0
  88. data/spec/dummy/public/app/main.map +1 -0
  89. data/spec/dummy/public/app/styles.bundle.js +364 -0
  90. data/spec/dummy/public/app/styles.map +1 -0
  91. data/spec/dummy/tmp/unsakini-ng2/LICENSE +21 -0
  92. data/spec/dummy/tmp/unsakini-ng2/README.md +1 -0
  93. data/spec/dummy/tmp/unsakini-ng2/angular-cli.json +59 -0
  94. data/spec/dummy/tmp/unsakini-ng2/e2e/app.e2e-spec.ts +14 -0
  95. data/spec/dummy/tmp/unsakini-ng2/e2e/app.po.ts +11 -0
  96. data/spec/dummy/tmp/unsakini-ng2/e2e/signup.e2e-spec.ts +28 -0
  97. data/spec/dummy/tmp/unsakini-ng2/e2e/signup.po.ts +31 -0
  98. data/spec/dummy/tmp/unsakini-ng2/e2e/tsconfig.json +16 -0
  99. data/spec/dummy/tmp/unsakini-ng2/karma.conf.js +45 -0
  100. data/spec/dummy/tmp/unsakini-ng2/package.json +49 -0
  101. data/spec/dummy/tmp/unsakini-ng2/protractor.conf.js +32 -0
  102. data/spec/dummy/tmp/unsakini-ng2/src/app/app.component.css +0 -0
  103. data/spec/dummy/tmp/unsakini-ng2/src/app/app.component.html +4 -0
  104. data/spec/dummy/tmp/unsakini-ng2/src/app/app.component.spec.ts +47 -0
  105. data/spec/dummy/tmp/unsakini-ng2/src/app/app.component.ts +10 -0
  106. data/spec/dummy/tmp/unsakini-ng2/src/app/app.module.ts +29 -0
  107. data/spec/dummy/tmp/unsakini-ng2/src/app/app.routes.module.ts +29 -0
  108. data/spec/dummy/tmp/unsakini-ng2/src/app/index.ts +2 -0
  109. data/spec/dummy/tmp/unsakini-ng2/src/app/registration/registration.component.css +0 -0
  110. data/spec/dummy/tmp/unsakini-ng2/src/app/registration/registration.component.html +14 -0
  111. data/spec/dummy/tmp/unsakini-ng2/src/app/registration/registration.component.spec.ts +157 -0
  112. data/spec/dummy/tmp/unsakini-ng2/src/app/registration/registration.component.ts +42 -0
  113. data/spec/dummy/tmp/unsakini-ng2/src/environments/environment.prod.ts +3 -0
  114. data/spec/dummy/tmp/unsakini-ng2/src/environments/environment.ts +8 -0
  115. data/spec/dummy/tmp/unsakini-ng2/src/favicon.ico +0 -0
  116. data/spec/dummy/tmp/unsakini-ng2/src/index.html +14 -0
  117. data/spec/dummy/tmp/unsakini-ng2/src/main.ts +12 -0
  118. data/spec/dummy/tmp/unsakini-ng2/src/polyfills.ts +19 -0
  119. data/spec/dummy/tmp/unsakini-ng2/src/styles.css +1 -0
  120. data/spec/dummy/tmp/unsakini-ng2/src/test.ts +31 -0
  121. data/spec/dummy/tmp/unsakini-ng2/src/tsconfig.json +18 -0
  122. data/spec/dummy/tmp/unsakini-ng2/src/typings.d.ts +2 -0
  123. data/spec/dummy/tmp/unsakini-ng2/tslint.json +114 -0
  124. data/spec/dummy/tmp/unsakini-ng2/typings.json +4 -0
  125. data/spec/factories/boards.rb +1 -1
  126. data/spec/factories/comments.rb +1 -1
  127. data/spec/factories/posts.rb +1 -1
  128. data/spec/factories/user_boards.rb +1 -1
  129. data/spec/factories/users.rb +1 -1
  130. data/spec/models/board_spec.rb +2 -2
  131. data/spec/models/comment_spec.rb +2 -2
  132. data/spec/models/post_spec.rb +2 -2
  133. data/spec/models/user_board_spec.rb +19 -19
  134. data/spec/models/user_spec.rb +1 -1
  135. data/spec/requests/{boards/boards_crud_spec.rb → api/boards/api_boards_crud_spec.rb} +26 -26
  136. data/spec/requests/{boards/boards_pagination_spec.rb → api/boards/api_boards_pagination_spec.rb} +7 -7
  137. data/spec/requests/{boards/private_board_spec.rb → api/boards/api_private_board_spec.rb} +26 -26
  138. data/spec/requests/{boards/shared_board_spec.rb → api/boards/api_shared_board_spec.rb} +9 -9
  139. data/spec/requests/{boards/sharing_board_spec.rb → api/boards/api_sharing_board_spec.rb} +13 -13
  140. data/spec/requests/{comments/comments_pagination_spec.rb → api/comments/api_comments_pagination_spec.rb} +3 -3
  141. data/spec/requests/{comments/comments_private_board_spec.rb → api/comments/api_comments_private_board_spec.rb} +20 -20
  142. data/spec/requests/{comments/comments_shared_board_spec.rb → api/comments/api_comments_shared_board_spec.rb} +17 -17
  143. data/spec/requests/{posts/posts_pagination_spec.rb → api/posts/api_posts_pagination_spec.rb} +3 -3
  144. data/spec/requests/{posts/posts_private_board_spec.rb → api/posts/api_posts_private_board_spec.rb} +22 -22
  145. data/spec/requests/{posts/posts_shared_board_spec.rb → api/posts/api_posts_shared_board_spec.rb} +24 -24
  146. data/spec/requests/{user/user_create_spec.rb → api/user/api_user_create_spec.rb} +23 -19
  147. data/spec/requests/{user/user_search_spec.rb → api/user/api_user_search_spec.rb} +9 -9
  148. data/spec/spec_helper.rb +0 -1
  149. data/spec/support/auth_helper.rb +2 -0
  150. metadata +202 -135
  151. data/angular/npm-debug.log +0 -54
  152. data/angular/src/app/confirm-account/confirm-account.component.ts +0 -27
  153. data/angular/src/app/confirm-account/confirm-account.html +0 -41
  154. data/angular/src/app/confirm-account/confirm-account.module.ts +0 -24
  155. data/angular/src/app/confirm-account/confirm-account.scss +0 -3
  156. data/angular/src/app/confirm-account/confirm-account.service.ts +0 -27
  157. data/angular/src/app/confirm-account/index.ts +0 -3
  158. data/angular/src/app/login/index.ts +0 -3
  159. data/angular/src/app/login/login.component.ts +0 -40
  160. data/angular/src/app/login/login.html +0 -43
  161. data/angular/src/app/login/login.module.ts +0 -27
  162. data/angular/src/app/login/login.service.ts +0 -48
  163. data/angular/src/app/registration/index.ts +0 -3
  164. data/angular/src/app/registration/registration.module.ts +0 -23
  165. data/angular/src/app/registration/registration.service.ts +0 -46
  166. data/angular/src/app/registration/registration.services.spec.ts +0 -71
  167. data/angular/src/app/services/auth-http/auth.http.service.ts +0 -35
  168. data/angular/src/app/services/auth-http/index.ts +0 -1
  169. data/angular/src/app/services/http/http.service.spec.ts +0 -205
  170. data/angular/src/app/services/http/http.service.ts +0 -40
  171. data/angular/src/app/services/http/index.ts +0 -1
  172. data/angular/src/app/services/index.ts +0 -3
  173. data/angular/src/app/services/services.module.ts +0 -33
  174. data/angular/src/assets/global.scss +0 -3
  175. data/angular/src/environments/custom.ts +0 -4
  176. data/app/controllers/concerns/unsakini/board_owner_controller_concern.rb +0 -42
  177. data/app/controllers/concerns/unsakini/comment_owner_controller_concern.rb +0 -36
  178. data/app/controllers/concerns/unsakini/logged_in_controller_concern.rb +0 -23
  179. data/app/controllers/concerns/unsakini/post_owner_controller_concern.rb +0 -38
  180. data/app/controllers/concerns/unsakini/serializer_controller_concern.rb +0 -13
  181. data/app/controllers/unsakini/base_controller.rb +0 -6
  182. data/app/controllers/unsakini/boards_controller.rb +0 -76
  183. data/app/controllers/unsakini/comments_controller.rb +0 -54
  184. data/app/controllers/unsakini/posts_controller.rb +0 -61
  185. data/app/controllers/unsakini/share_board_controller.rb +0 -122
  186. data/app/controllers/unsakini/user_token_controller.rb +0 -17
  187. data/app/controllers/unsakini/users_controller.rb +0 -69
  188. data/app/controllers/unsakini/web_controller.rb +0 -27
  189. data/app/mailers/unsakini/user_mailer.rb +0 -13
  190. data/app/models/concerns/unsakini/encryptable_model_concern.rb +0 -97
  191. data/app/models/unsakini/application_record.rb +0 -7
  192. data/app/models/unsakini/board.rb +0 -16
  193. data/app/models/unsakini/comment.rb +0 -12
  194. data/app/models/unsakini/post.rb +0 -15
  195. data/app/models/unsakini/user.rb +0 -43
  196. data/app/models/unsakini/user_board.rb +0 -84
  197. data/app/models/unsakini.rb +0 -5
  198. data/app/serializers/unsakini/board_serializer.rb +0 -7
  199. data/app/serializers/unsakini/post_serializer.rb +0 -26
  200. data/app/serializers/unsakini/user_board_serializer.rb +0 -14
  201. data/app/views/unsakini/user_mailer/confirm_account.html.erb +0 -3
  202. data/db/migrate/20161116114222_create_unsakini_boards.rb +0 -10
  203. data/db/migrate/20161126145352_create_unsakini_users.rb +0 -15
  204. data/lib/generators/unsakini/dependencies/USAGE +0 -5
  205. data/lib/generators/unsakini/dependencies/dependencies_generator.rb +0 -19
  206. data/public/images/unsakini.svg +0 -56
  207. data/public/unsakini/app/448c34a56d699c29117adc64c43affeb.woff2 +0 -0
  208. data/public/unsakini/app/89889688147bd7575d6327160d64e760.svg +0 -288
  209. data/public/unsakini/app/assets/global.scss +0 -3
  210. data/public/unsakini/app/e18bbf611f2a2e43afc071aa2f4e1512.ttf +0 -0
  211. data/public/unsakini/app/f4769f9bdb7466be65088239c12046d1.eot +0 -0
  212. data/public/unsakini/app/fa2772327f55d8198301fdb8bcfc8158.woff +0 -0
  213. data/public/unsakini/app/index.html +0 -14
  214. data/public/unsakini/app/inline.d41d8cd98f00b204e980.bundle.js +0 -2
  215. data/public/unsakini/app/inline.d41d8cd98f00b204e980.bundle.map +0 -1
  216. data/public/unsakini/app/main.54f49c65d3d20650a5d5.bundle.js +0 -2152
  217. data/public/unsakini/app/main.54f49c65d3d20650a5d5.bundle.js.gz +0 -0
  218. data/public/unsakini/app/main.54f49c65d3d20650a5d5.bundle.map +0 -1
  219. data/public/unsakini/app/styles.58e065928ed8ebd0b582.bundle.js +0 -2
  220. data/public/unsakini/app/styles.58e065928ed8ebd0b582.bundle.map +0 -1
  221. data/public/unsakini/app/styles.5dac0e986fce6f8738b300cb558b56a0.bundle.css +0 -8
  222. data/spec/dummy/config/initializers/knock.rb +0 -59
  223. data/spec/schema/jwt.json +0 -9
@@ -4,6 +4,5 @@
4
4
  // The list of which env maps to which file can be found in `angular-cli.json`.
5
5
 
6
6
  export const environment = {
7
- production: false,
8
- api_base_url: '/'
7
+ production: false
9
8
  };
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <title>Angular</title>
6
- <base href="/unsakini/app/">
6
+ <base href="/app/">
7
7
 
8
8
  <meta name="viewport" content="width=device-width, initial-scale=1">
9
9
  <link rel="icon" type="image/x-icon" href="favicon.ico">
@@ -0,0 +1 @@
1
+ /* You can add global styles to this file, and also import other style files */
@@ -0,0 +1,73 @@
1
+ class Api::BoardsController < ApplicationController
2
+
3
+
4
+ include LoggedInControllerConcern
5
+ include SerializerControllerConcern
6
+ include BoardOwnerControllerConcern
7
+ include ::ActionController::Serialization
8
+
9
+ before_action :ensure_board, :only => [:show, :update, :destroy]
10
+ before_action :ensure_board_owner, :only => [:update, :destroy]
11
+
12
+ # Returns boards belonging to current user
13
+ #
14
+ # `GET /api/boards`
15
+ #
16
+ def index
17
+ admin = true
18
+ shared = false
19
+ admin = params[:is_admin] == 'true' if params[:admin]
20
+ shared = params[:shared] == 'true' if params[:shared]
21
+ result = @user.user_boards.shared(shared)
22
+ result = result.admin if admin
23
+ paginate json: result, per_page: 10
24
+ end
25
+
26
+ # Creates board belonging to current user.
27
+ #
28
+ # `POST /api/boards`
29
+ #
30
+ def create
31
+ @user_board = UserBoard.new(
32
+ name: params[:board][:name],
33
+ user_id: @user.id,
34
+ encrypted_password: params[:encrypted_password],
35
+ is_admin: true
36
+ )
37
+ if @user_board.save
38
+ render json: @user_board, status: :created
39
+ else
40
+ render json: @user_board.errors.full_messages, status: 422
41
+ end
42
+
43
+ end
44
+
45
+ # Render a single board.
46
+ #
47
+ # `GET /api/boards/:id`
48
+ #
49
+ def show
50
+ render json: @user_board
51
+ end
52
+
53
+ # Updates a single board.
54
+ #
55
+ # `PUT /api/boards/:id`
56
+ def update
57
+ if @user_board.update(name: params[:board][:name], encrypted_password: params[:encrypted_password])
58
+ render json: @user_board
59
+ else
60
+ errors = @board.errors.full_messages.concat @user_board.errors.full_messages
61
+ render json: errors, status: 422
62
+ end
63
+ end
64
+
65
+ # Deletes a board resource.
66
+ #
67
+ # `DELETE /api/boards/:id`
68
+ def destroy
69
+ @board.destroy
70
+ render json: {}, status: :ok
71
+ end
72
+
73
+ end
@@ -0,0 +1,51 @@
1
+ class Api::CommentsController < ApplicationController
2
+
3
+ include LoggedInControllerConcern
4
+ include PostOwnerControllerConcern
5
+ include CommentOwnerControllerConcern
6
+ include ::ActionController::Serialization
7
+
8
+ before_action :ensure_post, only: [:index, :create]
9
+ before_action :ensure_comment, only: [:show, :update, :destroy]
10
+ before_action :ensure_comment_owner, only: [:update, :destroy]
11
+
12
+ # Renders the comments belonging to the post
13
+ #
14
+ # `GET /api/boards/:board_id/posts/:post_id/`
15
+ def index
16
+ paginate json: @post.comments.page(params[:page]), per_page: 20
17
+ end
18
+
19
+ # Creates new comment belonging to the post
20
+ #
21
+ # `POST /api/boards/:board_id/posts/:post_id/`
22
+ def create
23
+ @comment = Comment.new(params.permit(:content))
24
+ @comment.user = @user
25
+ @comment.post = @post
26
+ if @comment.save
27
+ render json: @comment
28
+ else
29
+ render json: @comment.errors, status: 422
30
+ end
31
+ end
32
+
33
+ # Updates a comment
34
+ #
35
+ # `PUT /api/boards/:board_id/posts/:post_id/comments/:id`
36
+ def update
37
+ if @comment.update(params.permit(:content))
38
+ render json: @comment
39
+ else
40
+ render json: @comment.errors, status: 422
41
+ end
42
+ end
43
+
44
+ # Deletes a comment
45
+ #
46
+ # `DELETE /api/boards/:board_id/posts/:post_id/comments/:id`
47
+ def destroy
48
+ @comment.destroy
49
+ render json: {}, status: :ok
50
+ end
51
+ end
@@ -0,0 +1,58 @@
1
+ class Api::PostsController < ApplicationController
2
+
3
+ include LoggedInControllerConcern
4
+ include BoardOwnerControllerConcern
5
+ include PostOwnerControllerConcern
6
+ include ::ActionController::Serialization
7
+
8
+ before_action :ensure_board, only: [:index, :create]
9
+ before_action :ensure_post, only: [:show, :update, :destroy]
10
+ before_action :ensure_post_owner, only: [:update, :destroy]
11
+
12
+ # Renders the post belonging to the board
13
+ #
14
+ # `GET /api/boards/:board_id/posts`
15
+ def index
16
+ paginate json: @board.posts.page(params[:page]), per_page: 20
17
+ end
18
+
19
+ # Renders a single post belonging to the board
20
+ #
21
+ # `GET /api/boards/:board_id/posts/:id`
22
+ def show
23
+ render json: @post
24
+ end
25
+
26
+ # Creates a single post belonging to the board
27
+ #
28
+ # `POST /api/boards/:board_id/posts/`
29
+ def create
30
+ @post = Post.new(params.permit(:title, :content, :board_id))
31
+ @post.user = @user
32
+ if (@post.save)
33
+ render json: @post, status: :created
34
+ else
35
+ render json: @post.errors, status: 422
36
+ end
37
+ end
38
+
39
+ # Updates a single post belonging to the board
40
+ #
41
+ # `PUT /api/boards/:board_id/posts/:id`
42
+ def update
43
+ if (@post.update(params.permit(:title, :content)))
44
+ render json: @post, status: :ok
45
+ else
46
+ render json: @post.errors, status: 422
47
+ end
48
+ end
49
+
50
+ # Deletes a single post belonging to the board
51
+ #
52
+ # `DELETE /api/boards/:board_id/posts/:id`
53
+ def destroy
54
+ @post.destroy
55
+ render json: {}, status: :ok
56
+ end
57
+
58
+ end
@@ -0,0 +1,118 @@
1
+ class Api::ShareBoardController < ApplicationController
2
+
3
+ include LoggedInControllerConcern
4
+ include BoardOwnerControllerConcern
5
+ include PostOwnerControllerConcern
6
+ include CommentOwnerControllerConcern
7
+
8
+ before_action :validate_params
9
+
10
+ # Shares a board to other users. Example payload param:
11
+ #
12
+ # `POST /api/share/board`
13
+ #
14
+ # ```
15
+ # {
16
+ # board: {
17
+ # id: 1,
18
+ # name: 'some encrypted text',
19
+ # },
20
+ # posts: [
21
+ # {
22
+ # board_id: 1,
23
+ # title: 'some encrypted text',
24
+ # content: 'some encrypted text',
25
+ # comments: [
26
+ # {
27
+ # id: 1,
28
+ # content: 'some encrypted text',
29
+ # user_id: 1,
30
+ # post_id: 1,
31
+ # }
32
+ # ]
33
+ # }
34
+ # ],
35
+ # shared_user_ids: [1, 2, 3, 4],
36
+ # encrypted_password: 'some encrypted password'
37
+ # }
38
+ # ```
39
+ # The `encrypted_password` param will be used to decrypt contents of this board. The encryption happens in the client so
40
+ # 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.
41
+ #
42
+ # `posts` and `comments` fields can be empty.
43
+ def index
44
+ ActiveRecord::Base.transaction do
45
+ if params[:posts]
46
+ params[:posts].each do |post|
47
+ p = Post.find(post[:id])
48
+ p.title = post[:title]
49
+ p.content = post[:content]
50
+ p.save!
51
+
52
+ if post[:comments] and p.valid?
53
+ post[:comments].each do |comment|
54
+ c = Comment.find(comment[:id])
55
+ c.content = comment[:content]
56
+ c.save!
57
+ end
58
+ end
59
+ end
60
+ end
61
+ if @user_board.share(params[:shared_user_ids], params[:encrypted_password])
62
+ render json: {}, status: :ok
63
+ else
64
+ raise "An error occured"
65
+ end
66
+ end
67
+ rescue
68
+ # clean up the created {UserBoard}s
69
+ render json: ["Some of the data can't be saved."], status: 422
70
+ end
71
+
72
+ # Validates the contents of params against the database records.
73
+ def validate_params
74
+
75
+ if params[:encrypted_password].nil? or params[:shared_user_ids].nil? or params[:board].nil?
76
+ render json: {}, status: 422
77
+ return
78
+ end
79
+
80
+ result = has_board_access(params[:board][:id])
81
+ if result[:status] != :ok
82
+ render json: {}, status: result[:status]
83
+ return
84
+ else
85
+ if !result[:user_board].is_admin
86
+ render json: {}, status: :forbidden
87
+ return
88
+ end
89
+ @board = result[:board]
90
+ @user_board = result[:user_board]
91
+ end
92
+
93
+ if params[:posts]
94
+
95
+ params[:posts].each do |post|
96
+ s = has_post_access(params[:board][:id], post[:id])[:status]
97
+ if s != :ok
98
+ render json: {}, status: s
99
+ return
100
+ end
101
+
102
+ if post[:comments]
103
+ post[:comments].each do |comment|
104
+ s = has_comment_access(post[:id], comment[:id])[:status]
105
+ if s != :ok
106
+ render json: {}, status: s
107
+ return
108
+ end
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+
118
+ end
@@ -0,0 +1,40 @@
1
+ class Api::UsersController < ApplicationController
2
+
3
+ include LoggedInControllerConcern
4
+ include ::ActionController::Serialization
5
+
6
+ skip_before_action :authenticate_user, only: [:create]
7
+ skip_before_action :set_user, only: [:create]
8
+
9
+ #Creates a new user
10
+ def create
11
+ @user = User.new(params.permit(:name, :email, :password, :password_confirmation))
12
+ if @user.save
13
+ render json: @user, status: :created
14
+ else
15
+ render json: @user.errors.full_messages, status: 422
16
+ end
17
+ end
18
+
19
+ # Renders the current user as json
20
+ #
21
+ # `GET /api/user/:id`
22
+ #
23
+ def show
24
+ render json: @user
25
+ end
26
+
27
+ # Returns the user with matching email
28
+ #
29
+ # `GET /api/users/search?email=xxx`
30
+ #
31
+ def search
32
+ user = User.where("email = ? AND id != ?", params[:email], @user.id).first
33
+ if user
34
+ render json: user
35
+ else
36
+ render json: {}, status: :not_found
37
+ end
38
+ end
39
+
40
+ end
@@ -1,5 +1,5 @@
1
1
  # Base controller for API controllers
2
2
 
3
- # class ApplicationController < ActionController::Base
3
+ class ApplicationController < ActionController::API
4
4
 
5
- # end
5
+ end
@@ -0,0 +1,38 @@
1
+ #Ensure user has access to the board and sets the `@board` variable in the controller
2
+ module BoardOwnerControllerConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ #Ensure user has access to the board and sets the `@board` variable in the controller
6
+ def ensure_board
7
+ board_id = params[:board_id] || params[:id]
8
+ result = has_board_access(board_id)
9
+ @board = result[:board]
10
+ @user_board = result[:user_board]
11
+ render json: {}, status: result[:status] if result[:status] != :ok
12
+ end
13
+
14
+ # Validate if user has access to board
15
+ #
16
+ # @param board_id [Integer] board id
17
+ def has_board_access(board_id)
18
+ board = nil
19
+ if !board_id.nil?
20
+ board = Board.find_by_id(board_id)
21
+ else
22
+ return {status: :bad_request}
23
+ end
24
+ if (board)
25
+ user_board = UserBoard.where(user_id: @user.id, board_id: board_id).first
26
+ return {status: :forbidden }if user_board.nil?
27
+ return {status: :ok, board: board, user_board: user_board}
28
+ else
29
+ return {status: :not_found}
30
+ end
31
+ end
32
+
33
+ #Ensures user is owner of the board. Must be run after {#ensure_board} method.
34
+ def ensure_board_owner
35
+ render json: {}, status: :forbidden if !@user_board.is_admin
36
+ end
37
+
38
+ end
@@ -0,0 +1,33 @@
1
+ # Ensures user is owner of the comment and sets the `@comment` variable in the controllers
2
+ module CommentOwnerControllerConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ # Ensures user is owner of the comment and sets the `@comment` variable in the controllers
6
+ def ensure_comment
7
+ post_id = params[:post_id]
8
+ comment_id = params[:comment_id] || params[:id]
9
+ result = has_comment_access post_id, comment_id
10
+ @comment = result[:comment]
11
+ status = result[:status]
12
+ render json: {}, status: status if status != :ok
13
+ end
14
+
15
+ # Validate if user has access to comment in the post
16
+ #
17
+ # @param post_id [Integer] post id
18
+ # @param comment_id [Integer] comment id
19
+ def has_comment_access(post_id, comment_id)
20
+ comment = Comment.where(id: comment_id, post_id: post_id, user_id: @user.id).first
21
+ if comment.nil?
22
+ return {status: :forbidden, comment: comment}
23
+ else
24
+ return {status: :ok, comment: comment}
25
+ end
26
+ end
27
+
28
+ # Ensures user is the owner of the comment. Must be run after {#ensure_comment} method.
29
+ def ensure_comment_owner
30
+ render json: {}, status: :forbidden if @comment.user_id != @user.id
31
+ end
32
+
33
+ end
@@ -0,0 +1,21 @@
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 LoggedInControllerConcern
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include Knock::Authenticable
10
+ before_action :authenticate_user
11
+ before_action :set_user
12
+ end
13
+
14
+ private
15
+ # Sets the `@user` variable in the controllers
16
+ def set_user
17
+ render json: {}, status: :unauthorized if current_user.nil?
18
+ @user = current_user
19
+ end
20
+
21
+ end
@@ -0,0 +1,36 @@
1
+ # Ensures user is owner of the post and sets the `@post` variable in the controllers
2
+ module PostOwnerControllerConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ # Ensures user is owner of the post and sets the `@post` variable in the controllers
6
+ def ensure_post
7
+ post_id = params[:post_id] || params[:id]
8
+ board_id = params[:board_id]
9
+ result = has_post_access(board_id, post_id)
10
+ status = result[:status]
11
+ @post = result[:post]
12
+ render json: {}, status: status if status != :ok
13
+ end
14
+
15
+ # Validate if user has access to the post in the board
16
+ #
17
+ # @param board_id [Integer] board id
18
+ # @param post_id [Integer] post id
19
+ def has_post_access(board_id, post_id)
20
+ post = Post.where(id: post_id, board_id: board_id)
21
+ .joins("LEFT JOIN user_boards ON user_boards.board_id = posts.board_id")
22
+ .where("user_boards.user_id = ?", @user.id)
23
+ .first
24
+ if post.nil?
25
+ return {status: :forbidden}
26
+ else
27
+ return {status: :ok, post: post}
28
+ end
29
+ end
30
+
31
+ # Ensures user is owner of the post. Must be run after {#ensure_post}`.
32
+ def ensure_post_owner
33
+ render json: {}, status: :forbidden if @post.user_id != @user.id
34
+ end
35
+
36
+ end
@@ -0,0 +1,11 @@
1
+ module SerializerControllerConcern
2
+
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include ::ActionController::Serialization
7
+ # respond_to :json
8
+ end
9
+
10
+
11
+ end
@@ -0,0 +1,2 @@
1
+ class UserTokenController < Knock::AuthTokenController
2
+ end
@@ -0,0 +1,23 @@
1
+ # Base controller for web pages
2
+
3
+ class WebBaseController < ActionController::Base
4
+ include ActionController::ImplicitRender
5
+ include ActionView::Layouts
6
+
7
+ # Renders welcome page
8
+ def index
9
+ @project = 'Unsakini'
10
+ @description = 'Opensource Encrypted Bulletin Board'
11
+ @version = Unsakini::VERSION
12
+ @author = 'Adones Pitogo'
13
+ @repository = 'https://github.com/adonespitogo/unsakini'
14
+ @title = "#{@project} | #{@description}"
15
+ @tagline = "Created by and for online activists, information security enthusiasts and government surveillance evaders."
16
+ @keywords = "unsakini, encrypted, bulletin board, BB, ruby, rails"
17
+ end
18
+
19
+ # Renders the angular index view when request url is /app/* to enable html5 pushState capability of angularjs
20
+ def app
21
+ render file: "#{Rails.root}/public/app/index.html", layout: false
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ # Base application model
2
+
3
+ class ApplicationRecord < ActiveRecord::Base
4
+ self.abstract_class = true
5
+ end
@@ -0,0 +1,14 @@
1
+ #Board model
2
+
3
+ class Board < ApplicationRecord
4
+ include EncryptableModelConcern
5
+ encryptable_attributes :name
6
+
7
+ validates :name, presence: true
8
+
9
+ has_many :users, through: :user_boards
10
+
11
+ has_many :user_boards, :dependent => :delete_all
12
+ has_many :posts, :dependent => :destroy
13
+
14
+ end
@@ -0,0 +1,9 @@
1
+ #Comment model
2
+ class Comment < ApplicationRecord
3
+ include EncryptableModelConcern
4
+ encryptable_attributes :content
5
+ validates :content, presence: true
6
+
7
+ belongs_to :post
8
+ belongs_to :user
9
+ end
@@ -0,0 +1,96 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ # Responsible for encryption and decryption of certain model attributes
5
+
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
@@ -0,0 +1,12 @@
1
+ #Post model
2
+
3
+ class Post < ApplicationRecord
4
+ include EncryptableModelConcern
5
+ encryptable_attributes :title, :content
6
+ validates :title, presence: true
7
+ validates :content, presence: true
8
+
9
+ belongs_to :user
10
+ belongs_to :board
11
+ has_many :comments, :dependent => :delete_all
12
+ end