From 4fc1714db8732955f9144e3fab0e8a8af916d32e Mon Sep 17 00:00:00 2001
From: shawn-higgins1 <23224097+shawn-higgins1@users.noreply.github.com>
Date: Wed, 31 Jul 2019 11:53:32 -0400
Subject: [PATCH] GRN2-176: Create a role editor that allows admins to specify
what permissions each role has (#709)
* Add roles editor
* Add colour selection ability to roles
* Add ability to assign roles to users in the UI
* Remove rolify and replace it with our own custom roles implemenation
* - Fix all existing roles functionality
- Fix super admins
* Fix bugs with new customers not have default roles
* Add can't create room setting
* Code improvements
* Fix migration
* Add tests for new methods
* Translate reserved role names
* Pull roles from saml/ldap
* Fix rspec
* Fix scrutinizer issues
* Fix email promoted/demoted tests
* Apply comments
* Redirect directly to the main room
* Add comments
---
.gitignore | 3 +
Gemfile | 3 +-
Gemfile.lock | 5 +-
app/assets/javascripts/admins.js | 126 +++++---
app/assets/javascripts/application.js | 2 +
app/assets/javascripts/room.js.erb | 5 +
app/assets/javascripts/user_edit.js | 88 ++++++
app/assets/stylesheets/admins.scss | 25 ++
app/assets/stylesheets/application.scss | 1 +
app/assets/stylesheets/users.scss | 4 +
.../utilities/_primary_themes.scss | 11 +
app/controllers/admins_controller.rb | 179 +++++++++--
app/controllers/application_controller.rb | 6 +-
app/controllers/concerns/emailer.rb | 10 +-
app/controllers/recordings_controller.rb | 4 +-
app/controllers/rooms_controller.rb | 34 ++-
app/controllers/users_controller.rb | 67 ++++-
app/helpers/admins_helper.rb | 4 +
app/helpers/application_helper.rb | 20 +-
app/helpers/rooms_helper.rb | 4 +-
app/helpers/theming_helper.rb | 2 +-
app/helpers/users_helper.rb | 16 +
app/mailers/user_mailer.rb | 10 +-
app/models/ability.rb | 23 +-
app/models/role.rb | 26 +-
app/models/user.rb | 91 +++++-
.../admins/components/_menu_buttons.html.erb | 23 +-
app/views/admins/components/_roles.html.erb | 94 ++++++
app/views/admins/components/_users.html.erb | 14 +-
app/views/admins/roles.html.erb | 27 ++
app/views/rooms/cant_create_rooms.html.erb | 44 +++
app/views/shared/_header.html.erb | 21 +-
.../shared/components/_admins_role.html.erb | 24 +-
.../shared/components/_admins_tags.html.erb | 42 +--
.../shared/modals/_create_role_modal.html.erb | 44 +++
app/views/shared/settings/_account.html.erb | 22 ++
app/views/user_mailer/user_demoted.html.erb | 4 +-
app/views/user_mailer/user_demoted.text.erb | 4 +-
app/views/user_mailer/user_promoted.html.erb | 6 +-
app/views/user_mailer/user_promoted.text.erb | 6 +-
config/initializers/rolify.rb | 12 -
config/locales/en.yml | 50 +++-
config/routes.rb | 10 +-
db/migrate/20190726153012_add_custom_roles.rb | 110 +++++++
db/schema.rb | 100 ++++---
db/seeds.rb | 1 +
spec/controllers/admins_controller_spec.rb | 283 +++++++++++++++---
spec/controllers/rooms_controller_spec.rb | 41 +++
spec/controllers/users_controller_spec.rb | 89 ++++++
spec/helpers/admins_helper_spec.rb | 44 +++
spec/helpers/application_helper_spec.rb | 10 +
spec/helpers/users_helper_spec.rb | 44 +++
spec/models/role_spec.rb | 35 +++
spec/models/user_spec.rb | 66 ++++
test/mailers/previews/user_mailer_preview.rb | 8 +-
yarn.lock | 4 +
56 files changed, 1718 insertions(+), 333 deletions(-)
create mode 100644 app/assets/javascripts/user_edit.js
create mode 100644 app/views/admins/components/_roles.html.erb
create mode 100644 app/views/admins/roles.html.erb
create mode 100644 app/views/rooms/cant_create_rooms.html.erb
create mode 100644 app/views/shared/modals/_create_role_modal.html.erb
delete mode 100644 config/initializers/rolify.rb
create mode 100644 db/migrate/20190726153012_add_custom_roles.rb
create mode 100644 spec/helpers/admins_helper_spec.rb
create mode 100644 spec/helpers/users_helper_spec.rb
create mode 100644 spec/models/role_spec.rb
create mode 100644 yarn.lock
diff --git a/.gitignore b/.gitignore
index 6014a7aa65..a9ff8afb05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,6 +32,9 @@ vendor/bundle
.env
env
+# Ignore yarn configs
+/node_modules
+
# IDEs
.idea
.idea/**
diff --git a/Gemfile b/Gemfile
index 69d6650249..7e8331c349 100644
--- a/Gemfile
+++ b/Gemfile
@@ -27,6 +27,7 @@ gem 'mini_racer', platforms: :ruby
# Use jquery as the JavaScript library
gem 'jquery-rails', '~> 4.3.3'
+gem 'jquery-ui-rails'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
@@ -72,8 +73,6 @@ gem 'redcarpet'
# For health check endpoint
gem "health_check"
-# For providing user roles
-gem "rolify"
# For limiting access based on user roles
gem 'cancancan', '~> 2.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 89ccfcddd3..5e1901f318 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -149,6 +149,8 @@ GEM
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
+ jquery-ui-rails (6.0.1)
+ railties (>= 3.2.16)
json (2.2.0)
jwt (2.2.1)
libv8 (7.3.492.27.1)
@@ -257,7 +259,6 @@ GEM
syslog_protocol
request_store (1.4.1)
rack (>= 1.4)
- rolify (5.2.0)
rspec-core (3.8.2)
rspec-support (~> 3.8.0)
rspec-expectations (3.8.4)
@@ -363,6 +364,7 @@ DEPENDENCIES
i18n-language-mapping (~> 0.1.0)
jbuilder (~> 2.5)
jquery-rails (~> 4.3.3)
+ jquery-ui-rails
listen (~> 3.0.5)
lograge
mini_racer
@@ -381,7 +383,6 @@ DEPENDENCIES
recaptcha
redcarpet
remote_syslog_logger
- rolify
rspec-rails (~> 3.7)
rubocop
sassc-rails
diff --git a/app/assets/javascripts/admins.js b/app/assets/javascripts/admins.js
index 0b27efe0cc..8852156791 100644
--- a/app/assets/javascripts/admins.js
+++ b/app/assets/javascripts/admins.js
@@ -19,47 +19,60 @@ $(document).on('turbolinks:load', function(){
var action = $("body").data('action');
// Only run on the admins page.
- if (controller == "admins" && action == "index") {
- // show the modal with the correct form action url
- $(".delete-user").click(function(data){
- var uid = $(data.target).closest("tr").data("user-uid")
- var url = $("body").data("relative-root")
- if (!url.endsWith("/")) {
- url += "/"
- }
- url += "u/" + uid
- $("#delete-confirm").parent().attr("action", url)
- })
-
- //clear the role filter if user clicks on the x
- $(".clear-role").click(function() {
- var search = new URL(location.href).searchParams.get('search')
-
- var url = window.location.pathname + "?page=1"
-
- if (search) {
- url += "&search=" + search
- }
-
- window.location.replace(url);
- })
- }
-
- if (controller == "admins" && action == "site_settings") {
- loadColourSelectors()
- }
-
- // Only run on the admins edit user page.
- if (controller == "admins" && action == "edit_user") {
- $(".setting-btn").click(function(data){
- var url = $("body").data("relative-root")
- if (!url.endsWith("/")) {
- url += "/"
- }
- url += "admins?setting=" + data.target.id
-
- window.location.href = url
- })
+ if (controller == "admins") {
+ if(action == "index") {
+ // show the modal with the correct form action url
+ $(".delete-user").click(function(data){
+ var uid = $(data.target).closest("tr").data("user-uid")
+ var url = $("body").data("relative-root")
+ if (!url.endsWith("/")) {
+ url += "/"
+ }
+ url += "u/" + uid
+ $("#delete-confirm").parent().attr("action", url)
+ })
+
+ //clear the role filter if user clicks on the x
+ $(".clear-role").click(function() {
+ var search = new URL(location.href).searchParams.get('search')
+
+ var url = window.location.pathname + "?page=1"
+
+ if (search) {
+ url += "&search=" + search
+ }
+
+ window.location.replace(url);
+ })
+ }
+ else if(action == "site_settings"){
+ loadColourSelectors()
+ }
+ else if (action == "roles"){
+ // Refreshes the new role modal
+ $("#newRoleButton").click(function(){
+ $("#createRoleName").val("")
+ })
+
+ // Updates the colour picker to the correct colour
+ role_colour = $("#role-colorinput-regular").data("colour")
+ $("#role-colorinput-regular").css("background-color", role_colour);
+ $("#role-colorinput-regular").css("border-color", role_colour);
+
+ loadRoleColourSelector(role_colour, $("#role-colorinput-regular").data("disabled"));
+
+ // Loads the jquery sortable so users can manually sort roles
+ $("#rolesSelect").sortable({
+ items: "a:not(.sort-disabled)",
+ update: function() {
+ $.ajax({
+ url: $(this).data("url"),
+ type: 'PATCH',
+ data: $(this).sortable('serialize')
+ });
+ }
+ });
+ }
}
});
@@ -160,4 +173,35 @@ function loadColourSelectors() {
location.reload()
});
})
+}
+
+function loadRoleColourSelector(role_colour, disabled) {
+ if (!disabled) {
+ const pickrRoleRegular = new Pickr({
+ el: '#role-colorinput-regular',
+ theme: 'monolith',
+ useAsButton: true,
+ lockOpacity: true,
+ defaultRepresentation: 'HEX',
+ closeWithKey: 'Enter',
+ default: role_colour,
+
+ components: {
+ palette: true,
+ preview: true,
+ hue: true,
+ interaction: {
+ input: true,
+ save: true,
+ },
+ },
+ });
+
+ // On save update the colour input's background colour and update the role colour input
+ pickrRoleRegular.on("save", (color, instance) => {
+ $("#role-colorinput-regular").css("background-color", color.toHEXA().toString());
+ $("#role-colorinput-regular").css("border-color", color.toHEXA().toString());
+ $("#role-colour").val(color.toHEXA().toString());
+ });
+ }
}
\ No newline at end of file
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 9e2fd706bc..9767209a29 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -31,4 +31,6 @@
//= require tabler
//= require tabler.plugins
//= require jquery_ujs
+//= require jquery-ui/widget
+//= require jquery-ui/widgets/sortable
//= require_tree .
diff --git a/app/assets/javascripts/room.js.erb b/app/assets/javascripts/room.js.erb
index 139b5ec732..2b0e8a931a 100644
--- a/app/assets/javascripts/room.js.erb
+++ b/app/assets/javascripts/room.js.erb
@@ -39,6 +39,11 @@ $(document).on('turbolinks:load', function(){
}, 2000)
}
});
+
+ // Forces the wrapper to take the entire screen height if the user can't create rooms
+ if ($("#cant-create-room-wrapper").length){
+ $(".wrapper").css('height', '100%').css('height', '-=130px');
+ }
}
// Display and update all fields related to creating a room in the createRoomModal
diff --git a/app/assets/javascripts/user_edit.js b/app/assets/javascripts/user_edit.js
new file mode 100644
index 0000000000..ecd0756f71
--- /dev/null
+++ b/app/assets/javascripts/user_edit.js
@@ -0,0 +1,88 @@
+// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
+//
+// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
+//
+// This program is free software; you can redistribute it and/or modify it under the
+// terms of the GNU Lesser General Public License as published by the Free Software
+// Foundation; either version 3.0 of the License, or (at your option) any later
+// version.
+//
+// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License along
+// with BigBlueButton; if not, see .
+
+$(document).on('turbolinks:load', function(){
+ var controller = $("body").data('controller');
+ var action = $("body").data('action');
+ if ((controller == "admins" && action == "edit_user") || (controller == "users" && action == "edit")) {
+ $(".setting-btn").click(function(data){
+ var url = $("body").data("relative-root")
+ if (!url.endsWith("/")) {
+ url += "/"
+ }
+ url += "admins?setting=" + data.target.id
+
+ window.location.href = url
+ })
+
+ // Clear the role when the user clicks the x
+ $(".clear-role").click(clearRole)
+
+ // When the user selects an item in the dropdown add the role to the user
+ $("#role-select-dropdown").change(function(data){
+ var dropdown = $("#role-select-dropdown");
+ var select_role_id = dropdown.val();
+
+ if(select_role_id){
+ // Disable the role in the dropdown
+ var selected_role = dropdown.find('[value=\"' + select_role_id + '\"]');
+ selected_role.prop("disabled", true)
+
+ // Add the role tag
+ var tag_container = $("#role-tag-container");
+ tag_container.append("" +
+ selected_role.text() + "");
+
+ // Update the role ids input that gets submited on user update
+ var role_ids = $("#user_role_ids").val()
+ role_ids += " " + select_role_id
+ $("#user_role_ids").val(role_ids)
+
+ // Add the clear role function to the tag
+ $("#user-role-tag_" + select_role_id).click(clearRole);
+
+ // Reset the dropdown
+ dropdown.val(null)
+ }
+ })
+ }
+})
+
+// This function removes the specfied role from a user
+function clearRole(data){
+ // Get the role id
+ var role_id = $(data.target).data("role-id");
+ var role_tag = $("#user-role-tag_" + role_id);
+
+ // Remove the role tag
+ $(role_tag).remove()
+
+ // Update the role ids input
+ var role_ids = $("#user_role_ids").val()
+ var parsed_ids = role_ids.split(' ')
+
+ var index = parsed_ids.indexOf(role_id.toString());
+
+ if (index > -1) {
+ parsed_ids.splice(index, 1);
+ }
+
+ $("#user_role_ids").val(parsed_ids.join(' '))
+
+ // Enable the role in the role select dropdown
+ var selected_role = $("#role-select-dropdown").find('[value=\"' + role_id + '\"]');
+ selected_role.prop("disabled", false)
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/admins.scss b/app/assets/stylesheets/admins.scss
index ee882a5d2f..6fe0260a46 100644
--- a/app/assets/stylesheets/admins.scss
+++ b/app/assets/stylesheets/admins.scss
@@ -54,4 +54,29 @@
height: 2rem;
width: 2rem;
}
+}
+
+.sort-disabled{
+ background: #e6e6e6 !important;
+ color: rgb(110, 118, 135) !important;
+ opacity: 0.75;
+ &:hover{
+ opacity: 0.9;
+ }
+}
+
+.form-disable{
+ background-color: #e6e6e6;
+}
+
+.role-colour-picker{
+ color: white !important;
+}
+
+.custom-role-tag{
+ color: white !important;
+}
+
+.user-role-tag{
+ color: white !important;
}
\ No newline at end of file
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 0b912ca4e5..2bda042ce1 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -32,6 +32,7 @@
@import "tabler/variables";
@import "bootstrap";
+@import "jquery-ui/sortable";
@import "tabler-custom";
@import "utilities/variables";
diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.scss
index 1daff6a91a..a0ec9aeece 100644
--- a/app/assets/stylesheets/users.scss
+++ b/app/assets/stylesheets/users.scss
@@ -18,3 +18,7 @@
// Place all the styles related to the Users controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
+
+.user-role-tag{
+ color: white !important;
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/utilities/_primary_themes.scss b/app/assets/stylesheets/utilities/_primary_themes.scss
index e94dc32808..b23f6db470 100644
--- a/app/assets/stylesheets/utilities/_primary_themes.scss
+++ b/app/assets/stylesheets/utilities/_primary_themes.scss
@@ -85,6 +85,11 @@ a {
&:hover {
color: $primary-color !important;
background-color: $primary-color-lighten !important;
+ &.sort-disabled {
+ background: #e6e6e6 !important;
+ color: rgb(110, 118, 135) !important;
+ opacity: 0.9;
+ }
}
&:active {
background-color: $primary-color-lighten !important;
@@ -102,6 +107,12 @@ input:focus, select:focus {
&, .list-group-item.active * {
color: $primary-color !important;
}
+
+ &.sort-disabled {
+ background: #e6e6e6 !important;
+ color: rgb(110, 118, 135) !important;
+ opacity: 0.9 !important;
+ }
}
.text-primary {
diff --git a/app/controllers/admins_controller.rb b/app/controllers/admins_controller.rb
index ac06e954c6..7ee7cc4488 100644
--- a/app/controllers/admins_controller.rb
+++ b/app/controllers/admins_controller.rb
@@ -36,7 +36,8 @@ def index
@search = params[:search] || ""
@order_column = params[:column] && params[:direction] != "none" ? params[:column] : "created_at"
@order_direction = params[:direction] && params[:direction] != "none" ? params[:direction] : "DESC"
- @role = params[:role] || ""
+
+ @role = params[:role] ? Role.find_by(name: params[:role], provider: @user_domain) : nil
@pagy, @users = pagy(user_list)
end
@@ -64,24 +65,6 @@ def server_recordings
def edit_user
end
- # POST /admins/promote/:user_uid
- def promote
- @user.add_role :admin
-
- send_user_promoted_email(@user)
-
- redirect_to admins_path, flash: { success: I18n.t("administrator.flash.promoted") }
- end
-
- # POST /admins/demote/:user_uid
- def demote
- @user.remove_role :admin
-
- send_user_demoted_email(@user)
-
- redirect_to admins_path, flash: { success: I18n.t("administrator.flash.demoted") }
- end
-
# POST /admins/ban/:user_uid
def ban_user
@user.roles = []
@@ -185,6 +168,158 @@ def default_recording_visibility
}
end
+ # ROLES
+
+ # GET /admins/roles
+ def roles
+ @roles = Role.editable_roles(@user_domain)
+
+ if @roles.count.zero?
+ Role.create_default_roles(@user_domain)
+ @roles = Role.editable_roles(@user_domain)
+ end
+
+ @selected_role = if params[:selected_role].nil?
+ @roles.find_by(name: 'user')
+ else
+ @roles.find(params[:selected_role])
+ end
+ end
+
+ # POST /admin/role
+ # This method creates a new role scope to the users provider
+ def new_role
+ new_role_name = params[:role][:name]
+
+ # Make sure that the role name isn't a duplicate or a reserved name like super_admin
+ if Role.duplicate_name(new_role_name, @user_domain)
+ flash[:alert] = I18n.t("administrator.roles.duplicate_name")
+
+ return redirect_to admin_roles_path
+ end
+
+ # Make sure the role name isn't empty
+ if new_role_name.strip.empty?
+ flash[:alert] = I18n.t("administrator.roles.empty_name")
+
+ return redirect_to admin_roles_path
+ end
+
+ # Create the new role with the second highest priority
+ # This means that it will only be more important than the user role
+ # This also updates the user role to have the highest priority
+ new_role = Role.create(name: new_role_name, provider: @user_domain)
+ user_role = Role.find_by(name: 'user', provider: @user_domain)
+
+ new_role.priority = user_role.priority
+ user_role.priority += 1
+
+ new_role.save!
+ user_role.save!
+
+ redirect_to admin_roles_path(selected_role: new_role.id)
+ end
+
+ # PATCH /admin/roles/order
+ # This updates the priority of a site's roles
+ # Note: A lower priority role will always get used before a higher priority one
+ def change_role_order
+ user_role = Role.find_by(name: "user", provider: @user_domain)
+ admin_role = Role.find_by(name: "admin", provider: @user_domain)
+
+ current_user_role = current_user.highest_priority_role
+
+ # Users aren't allowed to update the priority of the admin or user roles
+ if params[:role].include?(user_role.id.to_s) || params[:role].include?(admin_role.id.to_s)
+ flash[:alert] = I18n.t("administrator.roles.invalid_order")
+
+ return redirect_to admin_roles_path
+ end
+
+ # Restrict users to only updating the priority for roles in their domain with a higher
+ # priority
+ params[:role].each do |id|
+ role = Role.find(id)
+ if role.priority <= current_user_role.priority || role.provider != @user_domain
+ flash[:alert] = I18n.t("administrator.roles.invalid_update")
+ return redirect_to admin_roles_path
+ end
+ end
+
+ # Update the roles priority including the user role
+ top_priority = 0
+
+ params[:role].each_with_index do |id, index|
+ new_priority = index + [current_user_role.priority, 0].max + 1
+ top_priority = new_priority
+ Role.where(id: id).update_all(priority: new_priority)
+ end
+
+ user_role.priority = top_priority + 1
+ user_role.save!
+ end
+
+ # POST /admin/role/:role_id
+ # This method updates the permissions assigned to a role
+ def update_role
+ role = Role.find(params[:role_id])
+ current_user_role = current_user.highest_priority_role
+
+ # Checks that it is valid for the provider to update the role
+ if role.priority <= current_user_role.priority || role.provider != @user_domain
+ flash[:alert] = I18n.t("administrator.roles.invalid_update")
+ return redirect_to admin_roles_path(selected_role: role.id)
+ end
+
+ role_params = params.require(:role).permit(:name)
+ permission_params = params.require(:role)
+ .permit(
+ :can_create_rooms,
+ :send_promoted_email,
+ :send_demoted_email,
+ :can_edit_site_settings,
+ :can_edit_roles,
+ :can_manage_users,
+ :colour
+ )
+
+ # Make sure if the user is updating the role name that the role name is valid
+ if role.name != role_params[:name] && !Role.duplicate_name(role_params[:name], @user_domain) &&
+ !role_params[:name].strip.empty?
+ role.name = role_params[:name]
+ elsif role.name != role_params[:name]
+ flash[:alert] = I18n.t("administrator.roles.duplicate_name")
+
+ return redirect_to admin_roles_path(selected_role: role.id)
+ end
+
+ role.update(permission_params)
+
+ role.save!
+
+ redirect_to admin_roles_path(selected_role: role.id)
+ end
+
+ # DELETE admins/role/:role_id
+ # This deletes a role
+ def delete_role
+ role = Role.find(params[:role_id])
+
+ # Make sure no users are assigned to the role and the role isn't a reserved role
+ # before deleting
+ if role.users.count.positive?
+ flash[:alert] = I18n.t("administrator.roles.role_has_users", user_count: role.users.count)
+ return redirect_to admin_roles_path(selected_role: role.id)
+ elsif Role::RESERVED_ROLE_NAMES.include?(role) || role.provider != @user_domain ||
+ role.priority <= current_user.highest_priority_role.priority
+ return redirect_to admin_roles_path(selected_role: role.id)
+ else
+ role.delete
+ end
+
+ redirect_to admin_roles_path
+ end
+
private
def find_user
@@ -202,10 +337,10 @@ def verify_admin_of_user
# Gets the list of users based on your configuration
def user_list
- initial_list = if current_user.has_cached_role? :super_admin
- User.where.not(id: current_user.id).includes(:roles)
+ initial_list = if current_user.has_role? :super_admin
+ User.where.not(id: current_user.id)
else
- User.without_role(:super_admin).where.not(id: current_user.id).includes(:roles)
+ User.without_role(:super_admin).where.not(id: current_user.id)
end
if Rails.configuration.loadbalanced_configuration
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 80f2892b01..6d407183ae 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -137,7 +137,7 @@ def default_meeting_options
# Checks to make sure that the admin has changed his password from the default
def check_admin_password
- if current_user&.has_cached_role?(:admin) && current_user&.greenlight_account? &&
+ if current_user&.has_role?(:admin) && current_user&.greenlight_account? &&
current_user&.authenticate(Rails.configuration.admin_password_default)
flash.now[:alert] = I18n.t("default_admin",
@@ -185,10 +185,10 @@ def set_user_domain
# Checks if the user is banned and logs him out if he is
def check_user_role
- if current_user&.has_cached_role? :denied
+ if current_user&.has_role? :denied
session.delete(:user_id)
redirect_to root_path, flash: { alert: I18n.t("registration.banned.fail") }
- elsif current_user&.has_cached_role? :pending
+ elsif current_user&.has_role? :pending
session.delete(:user_id)
redirect_to root_path, flash: { alert: I18n.t("registration.approval.fail") }
end
diff --git a/app/controllers/concerns/emailer.rb b/app/controllers/concerns/emailer.rb
index 4dc1457251..9146bb00a8 100644
--- a/app/controllers/concerns/emailer.rb
+++ b/app/controllers/concerns/emailer.rb
@@ -35,16 +35,16 @@ def send_password_reset_email(user)
UserMailer.password_reset(@user, reset_link, logo_image, user_color).deliver_now
end
- def send_user_promoted_email(user)
+ def send_user_promoted_email(user, role)
return unless Rails.configuration.enable_email_verification
- UserMailer.user_promoted(user, root_url, logo_image, user_color).deliver_now
+ UserMailer.user_promoted(user, role, root_url, logo_image, user_color).deliver_now
end
- def send_user_demoted_email(user)
+ def send_user_demoted_email(user, role)
return unless Rails.configuration.enable_email_verification
- UserMailer.user_demoted(user, root_url, logo_image, user_color).deliver_now
+ UserMailer.user_demoted(user, role, root_url, logo_image, user_color).deliver_now
end
# Sends inivitation to join
@@ -87,7 +87,7 @@ def user_verification_link
end
def admin_emails
- admins = User.with_role(:admin)
+ admins = User.all_users_with_roles.where(roles: { can_manage_users: true })
if Rails.configuration.loadbalanced_configuration
admins = admins.without_role(:super_admin)
diff --git a/app/controllers/recordings_controller.rb b/app/controllers/recordings_controller.rb
index 2c4446ae22..c82deff709 100644
--- a/app/controllers/recordings_controller.rb
+++ b/app/controllers/recordings_controller.rb
@@ -52,8 +52,8 @@ def find_room
def verify_room_ownership
if !current_user ||
!@room.owned_by?(current_user) ||
- !current_user.has_cached_role?(:admin) ||
- !current_user.has_cached_role?(:super_admin)
+ !current_user.has_role?(:admin) ||
+ !current_user.has_role?(:super_admin)
redirect_to root_path
end
end
diff --git a/app/controllers/rooms_controller.rb b/app/controllers/rooms_controller.rb
index 233b6c23ca..06e3262d9b 100644
--- a/app/controllers/rooms_controller.rb
+++ b/app/controllers/rooms_controller.rb
@@ -24,8 +24,8 @@ class RoomsController < ApplicationController
before_action :validate_accepted_terms, unless: -> { !Rails.configuration.terms }
before_action :validate_verified_email, except: [:show, :join],
unless: -> { !Rails.configuration.enable_email_verification }
- before_action :find_room, except: :create
- before_action :verify_room_ownership, except: [:create, :show, :join, :logout, :login]
+ before_action :find_room, except: [:create, :join_specific_room]
+ before_action :verify_room_ownership, except: [:create, :show, :join, :logout, :login, :join_specific_room]
before_action :verify_room_owner_verified, only: [:show, :join],
unless: -> { !Rails.configuration.enable_email_verification }
before_action :verify_user_not_admin, only: [:show]
@@ -60,11 +60,14 @@ def show
@anyone_can_start = JSON.parse(@room[:room_settings])["anyoneCanStart"]
if current_user && @room.owned_by?(current_user)
- @search, @order_column, @order_direction, recs =
- recordings(@room.bbb_id, @user_domain, params.permit(:search, :column, :direction), true)
-
- @pagy, @recordings = pagy_array(recs)
+ if current_user.highest_priority_role.can_create_rooms
+ @search, @order_column, @order_direction, recs =
+ recordings(@room.bbb_id, @user_domain, params.permit(:search, :column, :direction), true)
+ @pagy, @recordings = pagy_array(recs)
+ else
+ render :cant_create_rooms
+ end
else
# Get users name
@name = if current_user
@@ -138,6 +141,21 @@ def destroy
redirect_to current_user.main_room
end
+ # POST room/join
+ def join_specific_room
+ room_uid = params[:join_room][:url].split('/').last
+
+ begin
+ @room = Room.find_by(uid: room_uid)
+ rescue ActiveRecord::RecordNotFound
+ return redirect_to current_user.main_room, alert: I18n.t("room.no_room.invalid_room_uid")
+ end
+
+ return redirect_to current_user.main_room, alert: I18n.t("room.no_room.invalid_room_uid") if @room.nil?
+
+ redirect_to room_path(@room)
+ end
+
# POST /:room_uid/start
def start
# Join the user in and start the meeting.
@@ -275,7 +293,7 @@ def verify_room_owner_verified
end
def verify_user_not_admin
- redirect_to admins_path if current_user && current_user&.has_cached_role?(:super_admin)
+ redirect_to admins_path if current_user && current_user&.has_role?(:super_admin)
end
def auth_required
@@ -288,7 +306,7 @@ def room_limit_exceeded
# Does not apply to admin
# 15+ option is used as unlimited
- return false if current_user&.has_cached_role?(:admin) || limit == 15
+ return false if current_user&.has_role?(:admin) || limit == 15
current_user.rooms.count >= limit
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 779ed6f35a..c138cba8bf 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -144,12 +144,14 @@ def update
errors.each { |k, v| @user.errors.add(k, v) }
render :edit, params: { settings: params[:settings] }
end
- elsif user_params[:email] != @user.email && @user.update_attributes(user_params)
+ elsif user_params[:email] != @user.email && @user.update_attributes(user_params) && update_roles
@user.update_attributes(email_verified: false)
+
flash[:success] = I18n.t("info_update_success")
redirect_to redirect_path
- elsif @user.update_attributes(user_params)
+ elsif @user.update_attributes(user_params) && update_roles
update_locale(@user)
+
flash[:success] = I18n.t("info_update_success")
redirect_to redirect_path
else
@@ -255,4 +257,65 @@ def passes_invite_reqs
invitation[:present]
end
+
+ # Updates as user's roles
+ def update_roles
+ # Check that the user can edit roles
+ if current_user.highest_priority_role.can_edit_roles
+ new_roles = params[:user][:role_ids].split(' ').map(&:to_i)
+ old_roles = @user.roles.pluck(:id)
+
+ added_role_ids = new_roles - old_roles
+ removed_role_ids = old_roles - new_roles
+
+ added_roles = []
+ removed_roles = []
+ current_user_role = current_user.highest_priority_role
+
+ # Check that the user has the permissions to add all the new roles
+ added_role_ids.each do |id|
+ role = Role.find(id)
+
+ # Admins are able to add the admin role to other users. All other roles may only
+ # add roles with a higher priority
+ if (role.priority > current_user_role.priority || current_user_role.name == "admin") &&
+ role.provider == @user_domain
+ added_roles << role
+ else
+ flash[:alert] = I18n.t("administrator.roles.invalid_assignment")
+ return false
+ end
+ end
+
+ # Check that the user has the permissions to remove all the deleted roles
+ removed_role_ids.each do |id|
+ role = Role.find(id)
+
+ # Admins are able to remove the admin role from other users. All other roles may only
+ # remove roles with a higher priority
+ if (role.priority > current_user_role.priority || current_user_role.name == "admin") &&
+ role.provider == @user_domain
+ removed_roles << role
+ else
+ flash[:alert] = I18n.t("administrator.roles.invalid_removal")
+ return false
+ end
+ end
+
+ # Send promoted/demoted emails
+ added_roles.each { |role| send_user_promoted_email(@user, role.name) if role.send_promoted_email }
+ removed_roles.each { |role| send_user_demoted_email(@user, role.name) if role.send_demoted_email }
+
+ # Update the roles
+ @user.roles.delete(removed_roles)
+ @user.roles << added_roles
+
+ # Make sure each user always has at least the user role
+ @user.roles = [Role.find_by(name: "user", provider: @user_domain)] if @user.roles.count.zero?
+
+ @user.save!
+ else
+ true
+ end
+ end
end
diff --git a/app/helpers/admins_helper.rb b/app/helpers/admins_helper.rb
index 704afb9129..c517c9f664 100644
--- a/app/helpers/admins_helper.rb
+++ b/app/helpers/admins_helper.rb
@@ -78,4 +78,8 @@ def registration_method_string
def room_limit_number
Setting.find_or_create_by!(provider: user_settings_provider).get_value("Room Limit").to_i
end
+
+ def edit_disabled
+ @edit_disabled ||= @selected_role.priority <= current_user.highest_priority_role.priority
+ end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index c71965df00..e8136084de 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -107,7 +107,25 @@ def current_translations
# Returns the page that the logo redirects to when clicked on
def home_page
return root_path unless current_user
- return admins_path if current_user.has_cached_role? :super_admin
+ return admins_path if current_user.has_role? :super_admin
current_user.main_room
end
+
+ def role_colour(role)
+ role.colour || Rails.configuration.primary_color_default
+ end
+
+ def translated_role_name(role)
+ if role.name == "denied"
+ I18n.t("roles.banned")
+ elsif role.name == "pending"
+ I18n.t("roles.pending")
+ elsif role.name == "admin"
+ I18n.t("roles.admin")
+ elsif role.name == "user"
+ I18n.t("roles.user")
+ else
+ role.name
+ end
+ end
end
diff --git a/app/helpers/rooms_helper.rb b/app/helpers/rooms_helper.rb
index 26bfaf6587..009fd77d94 100644
--- a/app/helpers/rooms_helper.rb
+++ b/app/helpers/rooms_helper.rb
@@ -37,7 +37,7 @@ def room_limit_exceeded
# Does not apply to admin or users that aren't signed in
# 15+ option is used as unlimited
- return false if current_user&.has_cached_role?(:admin) || limit == 15
+ return false if current_user&.has_role?(:admin) || limit == 15
current_user.rooms.length >= limit
end
@@ -46,7 +46,7 @@ def current_room_exceeds_limit(room)
# Get how many rooms need to be deleted to reach allowed room number
limit = Setting.find_or_create_by!(provider: user_settings_provider).get_value("Room Limit").to_i
- return false if current_user&.has_cached_role?(:admin) || limit == 15
+ return false if current_user&.has_role?(:admin) || limit == 15
@diff = current_user.rooms.count - limit
@diff.positive? && current_user.rooms.pluck(:id).index(room.id) + 1 > limit
diff --git a/app/helpers/theming_helper.rb b/app/helpers/theming_helper.rb
index 054799805c..e3f073fbba 100644
--- a/app/helpers/theming_helper.rb
+++ b/app/helpers/theming_helper.rb
@@ -31,7 +31,7 @@ def user_color
# Returns the user's provider in the settings context
def user_settings_provider
- if Rails.configuration.loadbalanced_configuration && current_user && !current_user&.has_cached_role?(:super_admin)
+ if Rails.configuration.loadbalanced_configuration && current_user && !current_user&.has_role?(:super_admin)
current_user.provider
elsif Rails.configuration.loadbalanced_configuration
@user_domain
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index e93f83e2c4..c62f3d7e68 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -20,4 +20,20 @@ module UsersHelper
def recaptcha_enabled?
Rails.configuration.recaptcha_enabled
end
+
+ def disabled_roles(user)
+ current_user_role = current_user.highest_priority_role
+
+ # Admins are able to remove the admin role from other admins
+ # For all other roles they can only add/remove roles with a higher priority
+ disallowed_roles = if current_user_role.name == "admin"
+ Role.editable_roles(@user_domain).where("priority < #{current_user_role.priority}")
+ .pluck(:id)
+ else
+ Role.editable_roles(@user_domain).where("priority <= #{current_user_role.priority}")
+ .pluck(:id)
+ end
+
+ user.roles.by_priority.pluck(:id) | disallowed_roles
+ end
end
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
index cebebe9289..b080fd83cf 100644
--- a/app/mailers/user_mailer.rb
+++ b/app/mailers/user_mailer.rb
@@ -35,20 +35,22 @@ def password_reset(user, url, image, color)
mail to: user.email, subject: t('reset_password.subtitle')
end
- def user_promoted(user, url, image, color)
+ def user_promoted(user, role, url, image, color)
@url = url
@admin_url = url + "admins"
@image = image
@color = color
- mail to: user.email, subject: t('mailer.user.promoted.subtitle')
+ @role = role
+ mail to: user.email, subject: t('mailer.user.promoted.subtitle', role: role)
end
- def user_demoted(user, url, image, color)
+ def user_demoted(user, role, url, image, color)
@url = url
@root_url = url
@image = image
@color = color
- mail to: user.email, subject: t('mailer.user.demoted.subtitle')
+ @role = role
+ mail to: user.email, subject: t('mailer.user.demoted.subtitle', role: role)
end
def invite_email(name, email, url, image, color)
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 07ec83584a..d06263c932 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -24,10 +24,25 @@ def initialize(user)
cannot :manage, AdminsController
elsif user.has_role? :super_admin
can :manage, :all
- elsif user.has_role? :admin
- can :manage, :all
- elsif user.has_role? :user
- cannot :manage, AdminsController
+ else
+ highest_role = user.highest_priority_role
+ if highest_role.can_edit_site_settings
+ can [:index, :site_settings, :server_recordings, :branding, :coloring, :coloring_lighten, :coloring_darken,
+ :room_authentication, :registration_method, :room_limit, :default_recording_visibility], :admin
+ end
+
+ if highest_role.can_edit_roles
+ can [:index, :roles, :new_role, :change_role_order, :update_role, :delete_role], :admin
+ end
+
+ if highest_role.can_manage_users
+ can [:index, :roles, :edit_user, :promote, :demote, :ban_user, :unban_user,
+ :approve, :invite], :admin
+ end
+
+ if !highest_role.can_edit_site_settings && !highest_role.can_edit_roles && !highest_role.can_manage_users
+ cannot :manage, AdminsController
+ end
end
end
end
diff --git a/app/models/role.rb b/app/models/role.rb
index 856b92c682..a178fad4be 100644
--- a/app/models/role.rb
+++ b/app/models/role.rb
@@ -19,13 +19,25 @@
class Role < ApplicationRecord
has_and_belongs_to_many :users, join_table: :users_roles
- belongs_to :resource,
- polymorphic: true,
- optional: true
+ default_scope { order(:priority) }
+ scope :by_priority, -> { order(:priority) }
+ scope :editable_roles, ->(provider) { where(provider: provider).where.not(name: %w[super_admin denied pending]) }
- validates :resource_type,
- inclusion: { in: Rolify.resource_types },
- allow_nil: true
+ RESERVED_ROLE_NAMES = %w[super_admin admin pending denied user]
- scopify
+ def self.duplicate_name(name, provider)
+ RESERVED_ROLE_NAMES.include?(name) || Role.exists?(name: name, provider: provider)
+ end
+
+ def self.create_default_roles(provider)
+ Role.create(name: "user", provider: provider, priority: 1, can_create_rooms: true, colour: "#868e96")
+ Role.create(name: "admin", provider: provider, priority: 0, can_create_rooms: true, send_promoted_email: true,
+ send_demoted_email: true, can_edit_site_settings: true,
+ can_edit_roles: true, can_manage_users: true, colour: "#f1c40f")
+ Role.create(name: "pending", provider: provider, priority: -1, colour: "#17a2b8")
+ Role.create(name: "denied", provider: provider, priority: -1, colour: "#343a40")
+ Role.create(name: "super_admin", provider: provider, priority: -2, can_create_rooms: true,
+ send_promoted_email: true, send_demoted_email: true, can_edit_site_settings: true,
+ can_edit_roles: true, can_manage_users: true, colour: "#cd201f")
+ end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index f3426fe256..9f2a65b821 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -19,7 +19,6 @@
require 'bbb_api'
class User < ApplicationRecord
- rolify
include ::BbbApi
attr_accessor :reset_token
@@ -33,6 +32,8 @@ class User < ApplicationRecord
has_many :rooms
belongs_to :main_room, class_name: 'Room', foreign_key: :room_id, required: false
+ has_and_belongs_to_many :roles, join_table: :users_roles
+
validates :name, length: { maximum: 256 }, presence: true
validates :provider, presence: true
validate :check_if_email_can_be_blank
@@ -59,6 +60,7 @@ def from_omniauth(auth)
u.username = auth_username(auth) unless u.username
u.email = auth_email(auth)
u.image = auth_image(auth) unless u.image
+ auth_roles(u, auth)
u.email_verified = true
u.save!
end
@@ -99,6 +101,18 @@ def auth_image(auth)
auth['info']['image']
end
end
+
+ def auth_roles(user, auth)
+ unless auth['info']['roles'].nil?
+ roles = auth['info']['roles'].split(',')
+
+ role_provider = auth['provider'] == "bn_launcher" ? auth['info']['customer'] : "greenlight"
+ roles.each do |role_name|
+ role = Role.where(provider: role_provider, name: role_name).first
+ user.roles << role unless role.nil?
+ end
+ end
+ end
end
def self.admins_search(string, role)
@@ -112,16 +126,16 @@ def self.admins_search(string, role)
search_query = ""
role_search_param = ""
- if role.present?
- search_query = "(users.name LIKE :search OR email LIKE :search OR username LIKE :search" \
- " OR users.#{created_at_query} LIKE :search OR provider LIKE :search)" \
- " AND roles.name = :roles_search"
- role_search_param = role
- else
+ if role.nil?
search_query = "users.name LIKE :search OR email LIKE :search OR username LIKE :search" \
- " OR users.#{created_at_query} LIKE :search OR provider LIKE :search" \
+ " OR users.#{created_at_query} LIKE :search OR users.provider LIKE :search" \
" OR roles.name LIKE :roles_search"
- role_search_param = "%#{string}%".downcase
+ role_search_param = "%#{string}%"
+ else
+ search_query = "(users.name LIKE :search OR email LIKE :search OR username LIKE :search" \
+ " OR users.#{created_at_query} LIKE :search OR users.provider LIKE :search)" \
+ " AND roles.name = :roles_search"
+ role_search_param = role.name
end
search_param = "%#{string}%"
@@ -204,17 +218,14 @@ def activation_token
def admin_of?(user)
if Rails.configuration.loadbalanced_configuration
- # Pulls in the user roles if they weren't request in the original request
- # So the has_cached_role? doesn't always return false
- user.roles
- if has_cached_role? :super_admin
+ if has_role? :super_admin
id != user.id
else
- (has_cached_role? :admin) && (id != user.id) && (provider == user.provider) &&
- (!user.has_cached_role? :super_admin)
+ highest_priority_role.can_manage_users && (id != user.id) && (provider == user.provider) &&
+ (!user.has_role? :super_admin)
end
else
- ((has_cached_role? :admin) || (has_cached_role? :super_admin)) && (id != user.id)
+ (highest_priority_role.can_manage_users || (has_role? :super_admin)) && (id != user.id)
end
end
@@ -228,6 +239,50 @@ def self.new_token
SecureRandom.urlsafe_base64
end
+ # role functions
+ def highest_priority_role
+ roles.by_priority.first
+ end
+
+ def add_role(role)
+ unless has_role?(role)
+ role_provider = Rails.configuration.loadbalanced_configuration ? provider : "greenlight"
+
+ roles << Role.find_or_create_by(name: role, provider: role_provider)
+
+ save!
+ end
+ end
+
+ def remove_role(role)
+ if has_role?(role)
+ role_provider = Rails.configuration.loadbalanced_configuration ? provider : "greenlight"
+
+ roles.delete(Role.find_by(name: role, provider: role_provider))
+ save!
+ end
+ end
+
+ # This rule is disabled as the function name must be has_role?
+ # rubocop:disable Naming/PredicateName
+ def has_role?(role)
+ # rubocop:enable Naming/PredicateName
+ roles.exists?(name: role)
+ end
+
+ def self.with_role(role)
+ User.all_users_with_roles.where(roles: { name: role })
+ end
+
+ def self.without_role(role)
+ User.where.not(id: with_role(role).pluck(:id))
+ end
+
+ def self.all_users_with_roles
+ User.joins("INNER JOIN users_roles ON users_roles.user_id = users.id INNER JOIN roles " \
+ "ON roles.id = users_roles.role_id")
+ end
+
private
def create_reset_activation_digest(token)
@@ -251,6 +306,10 @@ def initialize_main_room
# Initialize the user to use the default user role
def assign_default_role
+ role_provider = Rails.configuration.loadbalanced_configuration ? provider : "greenlight"
+
+ Role.create_default_roles(role_provider) if Role.where(provider: role_provider).count.zero?
+
add_role(:user) if roles.blank?
end
diff --git a/app/views/admins/components/_menu_buttons.html.erb b/app/views/admins/components/_menu_buttons.html.erb
index 7dfcc8ffc6..395cc3d439 100644
--- a/app/views/admins/components/_menu_buttons.html.erb
+++ b/app/views/admins/components/_menu_buttons.html.erb
@@ -14,13 +14,24 @@
%>
- <%= link_to admins_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "index"}" do %>
- <%= t("administrator.users.title") %>
+ <% highest_role = current_user.highest_priority_role %>
+ <% highest_role.name %>
+ <% if highest_role.can_manage_users || highest_role.name == "super_admin" %>
+ <%= link_to admins_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "index"}" do %>
+ <%= t("administrator.users.title") %>
+ <% end %>
<% end %>
- <%= link_to admin_site_settings_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "site_settings"}" do %>
- <%= t("administrator.site_settings.title") %>
+ <% if highest_role.can_edit_site_settings || highest_role.name == "super_admin" %>
+ <%= link_to admin_recordings_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "server_recordings"}" do %>
+ <%= t("administrator.recordings.title") %>
+ <% end %>
+ <%= link_to admin_site_settings_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "site_settings"}" do %>
+ <%= t("administrator.site_settings.title") %>
+ <% end %>
<% end %>
- <%= link_to admin_recordings_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "server_recordings"}" do %>
- <%= t("administrator.recordings.title") %>
+ <% if highest_role.can_edit_roles || highest_role.name == "super_admin" %>
+ <%= link_to admin_roles_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "roles"}" do %>
+ <%= t("administrator.roles.title") %>
+ <% end %>
<% end %>
\ No newline at end of file
diff --git a/app/views/admins/components/_roles.html.erb b/app/views/admins/components/_roles.html.erb
new file mode 100644
index 0000000000..b88548cd56
--- /dev/null
+++ b/app/views/admins/components/_roles.html.erb
@@ -0,0 +1,94 @@
+<%
+# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
+# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
+# This program is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free Software
+# Foundation; either version 3.0 of the License, or (at your option) any later
+# version.
+#
+# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+# You should have received a copy of the GNU Lesser General Public License along
+# with BigBlueButton; if not, see .
+%>
+
+
+
+
+
+
+ <% @roles.each do |role| %>
+ <%= link_to admin_roles_path(selected_role: role.id),
+ class: "#{"sort-disabled" if role.name == "user" || role.name == "admin" || role.priority <= current_user.highest_priority_role.priority } dropdown-item list-group-item list-group-item-action #{"active" if @selected_role.id == role.id}",
+ id: dom_id(role) do %>
+ <%= translated_role_name(role) %>
+ <% end %>
+ <% end %>
+
<% if roles.include?("pending") %>
@@ -122,16 +122,6 @@
-
- <% if roles.include?("admin") %>
- <%= button_to admin_demote_path(user_uid: user.uid), class: "dropdown-item" do %>
- <%= t("administrator.users.settings.demote") %>
- <% end %>
- <% elsif roles.include?("user") %>
- <%= button_to admin_promote_path(user_uid: user.uid), class: "dropdown-item" do %>
- <%= t("administrator.users.settings.promote") %>
- <% end %>
- <% end %>
<%= button_to admin_ban_path(user_uid: user.uid), class: "dropdown-item" do %>
<%= t("administrator.users.settings.ban") %>
<% end %>
diff --git a/app/views/admins/roles.html.erb b/app/views/admins/roles.html.erb
new file mode 100644
index 0000000000..9ab84f5f54
--- /dev/null
+++ b/app/views/admins/roles.html.erb
@@ -0,0 +1,27 @@
+<%
+# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
+# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
+# This program is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free Software
+# Foundation; either version 3.0 of the License, or (at your option) any later
+# version.
+#
+# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+# You should have received a copy of the GNU Lesser General Public License along
+# with BigBlueButton; if not, see .
+%>
+
+
diff --git a/app/views/rooms/cant_create_rooms.html.erb b/app/views/rooms/cant_create_rooms.html.erb
new file mode 100644
index 0000000000..f3dbc98da4
--- /dev/null
+++ b/app/views/rooms/cant_create_rooms.html.erb
@@ -0,0 +1,44 @@
+
+
+<%
+# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
+# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
+# This program is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free Software
+# Foundation; either version 3.0 of the License, or (at your option) any later
+# version.
+#
+# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+# You should have received a copy of the GNU Lesser General Public License along
+# with BigBlueButton; if not, see .
+%>
+
+
+
+
+
+
+
+
+
<%= t("room.no_room.title") %>
+
+
+ <%= form_for(:join_room, url: join_room_path) do |f| %>
+
\ No newline at end of file
diff --git a/app/views/shared/_header.html.erb b/app/views/shared/_header.html.erb
index 3182142faa..9595ec0ea7 100755
--- a/app/views/shared/_header.html.erb
+++ b/app/views/shared/_header.html.erb
@@ -23,7 +23,7 @@
\ No newline at end of file
diff --git a/app/views/shared/modals/_create_role_modal.html.erb b/app/views/shared/modals/_create_role_modal.html.erb
new file mode 100644
index 0000000000..c2e504ff06
--- /dev/null
+++ b/app/views/shared/modals/_create_role_modal.html.erb
@@ -0,0 +1,44 @@
+<%
+# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
+# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
+# This program is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free Software
+# Foundation; either version 3.0 of the License, or (at your option) any later
+# version.
+#
+# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+# You should have received a copy of the GNU Lesser General Public License along
+# with BigBlueButton; if not, see .
+%>
+
+
+
+
+
+
+
+
<%= t("modal.create_role.title") %>
+
+
+ <%= form_for(:role, url: admin_new_role_path) do |f| %>
+