proxes 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +24 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +12 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +18 -0
  8. data/Gemfile +4 -0
  9. data/Gemfile.ci +15 -0
  10. data/Gemfile.dev +10 -0
  11. data/Gemfile.dev.lock +155 -0
  12. data/LICENSE.txt +8 -0
  13. data/README.md +83 -0
  14. data/Rakefile +9 -0
  15. data/Vagrantfile +46 -0
  16. data/bin/console +15 -0
  17. data/bin/setup +8 -0
  18. data/config.ru +64 -0
  19. data/config/logger.yml +3 -0
  20. data/gulpfile.js +80 -0
  21. data/lib/proxes.rb +3 -0
  22. data/lib/proxes/app.rb +48 -0
  23. data/lib/proxes/controllers/application.rb +53 -0
  24. data/lib/proxes/controllers/audit_logs.rb +34 -0
  25. data/lib/proxes/controllers/auth_identity.rb +21 -0
  26. data/lib/proxes/controllers/component.rb +108 -0
  27. data/lib/proxes/controllers/permissions.rb +10 -0
  28. data/lib/proxes/controllers/roles.rb +10 -0
  29. data/lib/proxes/controllers/users.rb +119 -0
  30. data/lib/proxes/db.rb +17 -0
  31. data/lib/proxes/helpers/authentication.rb +45 -0
  32. data/lib/proxes/helpers/component.rb +40 -0
  33. data/lib/proxes/helpers/indices.rb +16 -0
  34. data/lib/proxes/helpers/pundit.rb +39 -0
  35. data/lib/proxes/helpers/views.rb +41 -0
  36. data/lib/proxes/loggers/elasticsearch.rb +9 -0
  37. data/lib/proxes/models/audit_log.rb +12 -0
  38. data/lib/proxes/models/identity.rb +67 -0
  39. data/lib/proxes/models/permission.rb +17 -0
  40. data/lib/proxes/models/role.rb +14 -0
  41. data/lib/proxes/models/user.rb +57 -0
  42. data/lib/proxes/policies/application_policy.rb +20 -0
  43. data/lib/proxes/policies/audit_log_policy.rb +40 -0
  44. data/lib/proxes/policies/identity_policy.rb +24 -0
  45. data/lib/proxes/policies/permission_policy.rb +40 -0
  46. data/lib/proxes/policies/request/root_policy.rb +12 -0
  47. data/lib/proxes/policies/request/search_policy.rb +15 -0
  48. data/lib/proxes/policies/request/snapshot_policy.rb +12 -0
  49. data/lib/proxes/policies/request/stats_policy.rb +15 -0
  50. data/lib/proxes/policies/request_policy.rb +69 -0
  51. data/lib/proxes/policies/role_policy.rb +40 -0
  52. data/lib/proxes/policies/token_policy.rb +46 -0
  53. data/lib/proxes/policies/user_policy.rb +46 -0
  54. data/lib/proxes/rake_tasks.rb +59 -0
  55. data/lib/proxes/request.rb +51 -0
  56. data/lib/proxes/request/root.rb +10 -0
  57. data/lib/proxes/request/search.rb +37 -0
  58. data/lib/proxes/request/snapshot.rb +16 -0
  59. data/lib/proxes/request/stats.rb +30 -0
  60. data/lib/proxes/security.rb +59 -0
  61. data/lib/proxes/seed.rb +10 -0
  62. data/lib/proxes/services/logger.rb +50 -0
  63. data/lib/proxes/version.rb +4 -0
  64. data/migrate/001_tables.rb +47 -0
  65. data/migrate/002_audit_log.rb +11 -0
  66. data/package.json +34 -0
  67. data/proxes.gemspec +44 -0
  68. data/public/js/bundle.js +28988 -0
  69. data/src/scripts/app.js +10 -0
  70. data/views/404.haml +1 -0
  71. data/views/audit_logs/index.haml +18 -0
  72. data/views/error.haml +4 -0
  73. data/views/getting_started.haml +16 -0
  74. data/views/identity/login.haml +19 -0
  75. data/views/identity/register.haml +17 -0
  76. data/views/index.haml +3 -0
  77. data/views/layout.haml +48 -0
  78. data/views/partials/delete_form.haml +4 -0
  79. data/views/partials/form_control.haml +21 -0
  80. data/views/partials/navbar.haml +25 -0
  81. data/views/partials/notifications.haml +24 -0
  82. data/views/partials/pager.haml +19 -0
  83. data/views/partials/sidebar.haml +32 -0
  84. data/views/permissions/display.haml +24 -0
  85. data/views/permissions/edit.haml +11 -0
  86. data/views/permissions/form.haml +3 -0
  87. data/views/permissions/index.haml +14 -0
  88. data/views/permissions/new.haml +10 -0
  89. data/views/roles/display.haml +33 -0
  90. data/views/roles/edit.haml +11 -0
  91. data/views/roles/form.haml +1 -0
  92. data/views/roles/index.haml +17 -0
  93. data/views/roles/new.haml +10 -0
  94. data/views/users/display.haml +32 -0
  95. data/views/users/edit.haml +11 -0
  96. data/views/users/identity.haml +3 -0
  97. data/views/users/index.haml +20 -0
  98. data/views/users/new.haml +11 -0
  99. data/views/users/profile.haml +37 -0
  100. data/views/users/user.haml +3 -0
  101. metadata +424 -0
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import ProxesComponents from 'react-proxes-components/react-proxes-components';
4
+
5
+ ReactDOM.render(<ProxesComponents pollInterval="30000"/>, document.getElementById('react-dashboard'));
6
+
7
+ // ReactDOM.render(
8
+ // <Health store={new ESStore()}/>,
9
+ // document.getElementById('indexlist')
10
+ // );
@@ -0,0 +1 @@
1
+ %h2 4 oh 4
@@ -0,0 +1,18 @@
1
+ .row
2
+ .col-md-12
3
+ .panel.panel-default
4
+ %table.table.table-striped
5
+ %thead
6
+ %tr
7
+ %th User email
8
+ %th Action
9
+ %th Created at
10
+ %tbody
11
+ -list.each do |entity|
12
+ %tr
13
+ %td
14
+ %a{ href: "/_proxes/users/#{entity.user.id}" }= entity.user.email
15
+ %td
16
+ = entity.action
17
+ %td
18
+ = entity.created_at.strftime('%Y-%m-%d %H:%M:%S')
@@ -0,0 +1,4 @@
1
+ %h2 Whoops
2
+
3
+ %p.lead
4
+ = error.class
@@ -0,0 +1,16 @@
1
+ .row
2
+ .col-md-12
3
+ .panel.panel-success
4
+ .panel-heading
5
+ You can now start using this Elasticsearch cluster:
6
+ .panel-body
7
+ %h4 Send Data
8
+ %pre
9
+ :plain
10
+ curl -X POST '#{ request.base_url }/#{ current_user.email }/test' \
11
+ -u #{ current_user.email }:yourpassword -d '{"message":"Get cracking!"}'
12
+
13
+ %h4 Search
14
+ %pre
15
+ curl -X GET '#{ request.base_url }/_search' -u #{ current_user.email }:yourpassword
16
+
@@ -0,0 +1,19 @@
1
+ .row
2
+ .col-sm-3
3
+ .col-sm-6
4
+ .panel.panel-default
5
+ .panel-heading
6
+ ProxES Login
7
+ .panel-body
8
+ %form{ method: 'post', action: '/_proxes/auth/identity/callback' }
9
+ .form-group
10
+ %label.control-label Username
11
+ %input.form-control.border-input{ name: 'username' }
12
+ .form-group
13
+ %label.control-label Password
14
+ %input.form-control.border-input{ name: 'password', type: 'password' }
15
+ %button.btn.btn-primary{ type: 'submit' } Log In
16
+ .pull-right
17
+ No account yet?
18
+ %a.btn.btn-default{ href: '/auth/identity/register' } Register
19
+ .col-sm-3
@@ -0,0 +1,17 @@
1
+ .row
2
+ .col-md-12
3
+ %h1 ProxES Registration
4
+ .row
5
+ .col-md-2
6
+ .col-md-8
7
+ .panel.panel-default
8
+ .panel-heading
9
+ ProxES Registration
10
+ .panel-body
11
+ %form.form-horizontal{ method: 'post', action: '/_proxes/auth/identity/new' }
12
+ = form_control(:username, identity, label: 'Email', placeholder: 'Your email address')
13
+ = form_control(:password, identity, label: 'Password', type: :password)
14
+ = form_control(:password_confirmation, identity, label: 'Confirm Password', type: :password)
15
+
16
+ %button.btn.btn-primary{ type: 'submit' } Register
17
+ .col-md-2
@@ -0,0 +1,3 @@
1
+ = haml :getting_started
2
+
3
+ #react-dashboard
@@ -0,0 +1,48 @@
1
+ !!! 5
2
+ %html{ lang: 'en' }
3
+ %head
4
+ %meta{ charset: 'utf-8' }
5
+ %meta{ 'http-equiv' => 'X-UA-Compatible', 'content' => 'IE=edge,chrome=1' }
6
+ %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
7
+ %link{ rel: 'apple-touch-icon', sizes: '76x76', href: '/assets/img/apple-icon.png' }
8
+
9
+ %title
10
+ ProxES
11
+ - if defined? title
12
+ = "- #{title}"
13
+
14
+ %meta{ name: 'description', content: '' }
15
+ %meta{ name: 'author', content: '' }
16
+
17
+ / Le styles
18
+ %link{ rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css', media: 'screen' }
19
+ %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/3.3.7+1/css/sb-admin-2.min.css', media: 'screen' }
20
+ %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.css', media: 'screen' }
21
+ %link{ rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css', media: 'screen' }
22
+ /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
23
+ /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
24
+
25
+ %body
26
+ #wrapper
27
+ = haml :'partials/navbar', locals: { title: (defined?(title) ? title : 'ProxES') }
28
+ #page-wrapper
29
+ .row
30
+ .col-md-12
31
+ -if defined? title
32
+ %h1.page-header= title
33
+ = haml :'partials/notifications'
34
+ = yield
35
+ %footer.footer.text-muted.text-center
36
+ %hr
37
+ .copyright
38
+ :plain
39
+ &copy; <script>document.write(new Date().getFullYear())</script>, JadeIT
40
+
41
+
42
+ / Placed at the end of the document so the pages load faster
43
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js' }
44
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/3.3.7+1/js/sb-admin-2.min.js' }
45
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.js' }
46
+ %script{ type: 'text/javascript', src: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js' }
47
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.min.js' }
48
+ %script{ type: 'text/javascript', src: '/js/bundle.js' }
@@ -0,0 +1,4 @@
1
+ <form method="post" action="<%= base_path %>/<%= entity.id %>">
2
+ <input type="hidden" name="_method" value="DELETE">
3
+ <button type="submit" class="btn btn-danger"><%= delete_label %></button>
4
+ </form>
@@ -0,0 +1,21 @@
1
+ %div{ class: "form-group#{model.errors[field] ? ' has-error' : ''}" }
2
+ %label.col-sm-3.control-label{ for: attributes[:id] }= label
3
+ .col-sm-9
4
+ - type = attributes.delete(:type)
5
+ - if type == 'select'
6
+ - options = attributes.delete(:options)
7
+ %select{attributes}
8
+ - if attributes[:multiple]
9
+ - options.each do |k,v| k ||= v; v ||= k;
10
+ %option{ value: k, selected: (model.send(field).map(&:id).include? k)}= v
11
+ - else
12
+ %option{ value: ""} -- Select One --
13
+ - options.each do |k,v| k ||= v; v ||= k;
14
+ %option{ value: k, selected: (k.to_s == model[field].to_s)}= v
15
+ - elsif type == 'textarea'
16
+ %textarea{attributes}
17
+ = model[field]
18
+ - else
19
+ %input{attributes, type: type, value: model[field]}
20
+ - if model.errors[field]
21
+ %p.help-block.text-danger= model.errors[field].join(', ')
@@ -0,0 +1,25 @@
1
+ .navbar.navbar-default.navbar-static-top{ role: 'navigation', style: 'margin-bottom: 0' }
2
+ .navbar-header
3
+ %span.navbar-brand
4
+ ProxES
5
+ - if defined? title
6
+ = "- #{title}"
7
+ %button.navbar-toggle.collapsed{ 'type' => 'button', 'data-toggle' => 'collapse', 'data-target' => '#navbar', 'aria-expanded' => 'false', 'aria-controls' => 'navbar' }
8
+ %span.sr-only Toggle navigation
9
+ %span.icon-bar.bar1
10
+ %span.icon-bar.bar2
11
+ %span.icon-bar.bar3
12
+ -if current_user
13
+ %form.nav.navbar-top-links.navbar-form.navbar-right{ action: '/_proxes/auth/identity', method: 'post' }
14
+ %a.btn.btn-default{ href: "/_proxes/users/profile" } My Account
15
+ %input{ name: '_method', value: 'DELETE', type: 'hidden' }
16
+ %button.btn.btn-default{ type: 'submit' }
17
+ / %i.ti-panel
18
+ Logout
19
+ - else
20
+ %ul.nav.navbar-top-links.navbar-right
21
+ %li
22
+ %a.btn.btn-link{ href: '/auth/identity' }
23
+ Log In
24
+ .navbar-default.sidebar{ role: 'navigation' }
25
+ = haml :'partials/sidebar'
@@ -0,0 +1,24 @@
1
+ - if flash[:danger]
2
+ .alert.alert-danger.alert-dismissible{ role: "alert" }
3
+ %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
4
+ - (flash[:danger].is_a?(Array) ? flash[:danger] : [flash[:danger]]).each do |msg|
5
+ %span= msg
6
+
7
+ - if flash[:warning]
8
+ .alert.alert-warning.alert-dismissible{ role: "alert" }
9
+ %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
10
+ - (flash[:warning].is_a?(Array) ? flash[:warning] : [flash[:warning]]).each do |msg|
11
+ %span= msg
12
+
13
+ - if flash[:success]
14
+ .alert.alert-success.alert-dismissible{ role: "alert" }
15
+ %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
16
+ - (flash[:success].is_a?(Array) ? flash[:success] : [flash[:success]]).each do |msg|
17
+ %span= msg
18
+
19
+
20
+ - if flash[:info]
21
+ .alert.alert-info.alert-dismissible{ role: "alert" }
22
+ %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
23
+ - (flash[:info].is_a?(Array) ? flash[:info] : [flash[:info]]).each do |msg|
24
+ %span= msg
@@ -0,0 +1,19 @@
1
+ <nav aria-label="Page navigation">
2
+ <p class="text-center">
3
+ Showing <%= list.current_page_record_range %> of <%= list.pagination_record_count %> records
4
+ </p>
5
+ <ul class="pager">
6
+ <li>
7
+ <a href="<%= base_path %>?page=1&count=<%= list.page_size %>">First</a>
8
+ </li>
9
+ <li<% if list.first_page? %> class="disabled"<% end %>>
10
+ <a href="<%= prev_link %>">Previous</a>
11
+ </li>
12
+ <li<% if list.last_page? %> class="disabled"<% end %>>
13
+ <a href="<%= next_link %>">Next</a>
14
+ </li>
15
+ <li>
16
+ <a href="<%= base_path %>?page=<%= list.page_count %>&count=<%= list.page_size %>">Last</a>
17
+ </li>
18
+ </ul>
19
+ </nav>
@@ -0,0 +1,32 @@
1
+ %ul.nav.nav-pills.nav-stacked
2
+ - if defined?(current_user) && current_user
3
+ %li
4
+ %a{ href: '/_proxes' }
5
+ %i.fa.fa-dashboard.fa-fw
6
+ Dashboard
7
+ - if current_user.super_admin?
8
+ %li
9
+ %a{ href: '/_proxes/users' }
10
+ %i.fa.fa-user.fa-fw
11
+ Users
12
+ %li
13
+ %a{ href: '/_proxes/roles' }
14
+ %i.fa.fa-group.fa-fw
15
+ Roles
16
+ %li
17
+ %a{ href: '/_proxes/permissions' }
18
+ %i.fa.fa-check-square-o.fa-fw
19
+ Permissions
20
+ %li
21
+ %a{ href: '/_proxes/settings' }
22
+ %i.fa.fa-cog.fa-fw
23
+ Settings
24
+ - else
25
+ %li.active
26
+ %a{ href: '/auth/identity' }
27
+ %i.fa.fa-user.fa-fw
28
+ Log In
29
+ %li
30
+ %a{ href: '/auth/identity/register' }
31
+ %i.fa.fa-pencil-square-o.fa-fw
32
+ Register
@@ -0,0 +1,24 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %p.description
7
+ %label Role:
8
+ = entity.role.name
9
+ %p.description
10
+ %label Verb:
11
+ = entity.verb
12
+ %p.description
13
+ %label Pattern:
14
+ = entity.pattern
15
+
16
+ .row
17
+ .col-md-6
18
+ %a.btn.btn-default{ href: "/_proxes/permissions/#{entity.id}/edit" } Edit
19
+ .col-md-6.text-right
20
+ - if policy(entity).delete?
21
+ %form{ method: 'post', action: "/_proxes/permissions/#{entity.id}" }
22
+ %input{ name: '_method', value: 'DELETE', type: 'hidden' }
23
+ %button.btn.btn-warning{ type: 'submit' } Delete
24
+ .col-md-2
@@ -0,0 +1,11 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %form.form-horizontal{ method: 'post', action: "/_proxes/permissions/#{entity.id}" }
7
+ %input{ name: '_method', value: 'PUT', type: 'hidden' }
8
+ = haml :'permissions/form', locals: { entity: entity }
9
+ %button.btn.btn-primary{ type: 'submit' }
10
+ Update Permission
11
+ .col-md-2
@@ -0,0 +1,3 @@
1
+ = form_control(:role_id, entity, type: 'select', options: ProxES::Role.to_hash(:id, :name))
2
+ = form_control(:verb, entity, type: 'select', options: ProxES::Permission.verbs)
3
+ = form_control(:pattern, entity)
@@ -0,0 +1,14 @@
1
+ .row
2
+ .col-md-12
3
+ .panel.panel-default
4
+ .panel-heading VERB Pattern
5
+ %table.table.table-striped
6
+ %tbody
7
+ -list.each do |entity|
8
+ %tr
9
+ %td
10
+ %a.btn-block{ href: "/_proxes/permissions/#{entity.id}" }
11
+ = entity.verb
12
+ = entity.pattern
13
+ .panel-body.text-right
14
+ %a.btn.btn-primary{ href: '/_proxes/permissions/new' } New Permission
@@ -0,0 +1,10 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %form.form-horizontal{ method: 'post', action: '/_proxes/permissions' }
7
+ = haml :'permissions/form', locals: { entity: entity }
8
+ %button.btn.btn-primary.btn{ type: 'submit' }
9
+ Create Permission
10
+ .col-md-2
@@ -0,0 +1,33 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %p.description
7
+ %label Name:
8
+ = entity.name
9
+
10
+ .row
11
+ .col-md-6
12
+ %a.btn.btn-default{ href: "/_proxes/roles/#{entity.id}/edit" } Edit
13
+ .col-md-6.text-right
14
+ - if policy(entity).delete?
15
+ %form{ method: 'post', action: "/_proxes/roles/#{entity.id}" }
16
+ %input{ name: '_method', value: 'DELETE', type: 'hidden' }
17
+ %button.btn.btn-warning{ type: 'submit' } Delete
18
+ .col-md-2
19
+
20
+ .row
21
+ .col-md-2
22
+ .col-md-8
23
+ .panel.panel-default
24
+ .panel-heading
25
+ %h3 Permissions
26
+ %table.table
27
+ - entity.permissions.each do |permission|
28
+ %tr
29
+ %td
30
+ %a.btn-block{ href: "/_proxes/permissions/#{entity.id}" }
31
+ = permission.verb
32
+ = permission.pattern
33
+ .col-md-2
@@ -0,0 +1,11 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %form.form-horizontal{ method: 'post', action: "/_proxes/roles/#{entity.id}" }
7
+ %input{ name: '_method', value: 'PUT', type: 'hidden' }
8
+ = haml :'roles/form', locals: { entity: entity }
9
+ %button.btn.btn-primary{ type: 'submit' }
10
+ Update Role
11
+ .col-md-2
@@ -0,0 +1 @@
1
+ = form_control(:name, entity)
@@ -0,0 +1,17 @@
1
+ .row
2
+ .col-md-12
3
+ .panel.panel-default
4
+ %table.table.table-striped
5
+ %thead
6
+ %tr
7
+ %th Name
8
+ %th Permissions
9
+ %tbody
10
+ -list.each do |entity|
11
+ %tr
12
+ %td
13
+ %a{ href: "/_proxes/roles/#{entity.id}" }= entity.name
14
+ %td
15
+ = entity.permissions_dataset.count
16
+ .panel-body.text-right
17
+ %a.btn.btn-primary{ href: '/_proxes/roles/new' } New Role
@@ -0,0 +1,10 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %form.form-horizontal{ method: 'post', action: '/_proxes/roles' }
7
+ = haml :'roles/form', locals: { entity: entity }
8
+ %button.btn.btn-primary.btn{ type: 'submit' }
9
+ Create Role
10
+ .col-md-2
@@ -0,0 +1,32 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ .author
7
+ %img.pull-right.thumbnail{ src: entity.gravatar }
8
+ %h4.title= entity.email
9
+
10
+ %hr
11
+ %p.description
12
+ %label Name:
13
+ = entity.name
14
+ %p.description
15
+ %label Surname:
16
+ = entity.surname
17
+ %p.description
18
+ %label Roles:
19
+ = entity.roles_dataset.map(:name).map(&:titlecase).join(', ')
20
+ %p.description
21
+ %label Signed up:
22
+ = entity.created_at.strftime('%Y-%m-%d %H:%M:%S')
23
+
24
+ .row
25
+ .col-md-6
26
+ %a.btn.btn-default{ href: "/_proxes/users/#{entity.id}/edit" } Edit
27
+ .col-md-6.text-right
28
+ - if policy(entity).delete?
29
+ %form{ method: 'post', action: "/_proxes/users/#{entity.id}" }
30
+ %input{ name: '_method', value: 'DELETE', type: 'hidden' }
31
+ %button.btn.btn-warning{ type: 'submit' } Delete
32
+ .col-md-2