diff --git a/BrainPortal/app/controllers/application_controller.rb b/BrainPortal/app/controllers/application_controller.rb index 6ad51d9b4..7b447dd68 100644 --- a/BrainPortal/app/controllers/application_controller.rb +++ b/BrainPortal/app/controllers/application_controller.rb @@ -61,6 +61,7 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception, unless: -> { request.format.json? || request.format.xml? } + rescue_from CbrainLicenseException, with: :redirect_show_license ######################################################################## @@ -69,6 +70,15 @@ class ApplicationController < ActionController::Base private + + def redirect_show_license + if params[:id].present? && params[:controller] == "groups" + redirect_to show_license_group_path(params[:id]) + else + redirect_to groups_path + end + end + # Re-compute the host and IP from the request (when not logged in, or changed) def adjust_remote_ip_and_host #:nodoc: from_ip = cbrain_session[:guessed_remote_ip].presence || '(None)' # what we had previously diff --git a/BrainPortal/app/controllers/groups_controller.rb b/BrainPortal/app/controllers/groups_controller.rb index 7ee6f2c33..2d5e32208 100644 --- a/BrainPortal/app/controllers/groups_controller.rb +++ b/BrainPortal/app/controllers/groups_controller.rb @@ -33,6 +33,9 @@ class GroupsController < ApplicationController before_action :license_check, :only => [:show, :create, :switch, :edit, :update, :unregister, :destroy] + # few license related attributes are updated here + before_action :group_license_attributes, :only => [:show, :new_license, :add_license, :show_license, :sign_license] + # GET /groups # GET /groups.xml def index #:nodoc: @@ -268,6 +271,114 @@ def license_redirect #:nodoc: end end + def new_license #:nodoc: + cb_error "Only owner can set licensing", :redirect => { :action => :show } unless @can_add_license + end + + def add_license #:nodoc: + cb_error("Only owner can set licensing", :redirect => { :action => :show }) unless @can_add_license + + license_text = params[:license_text] + cb_error 'Empty licenses are presently not allowed' if license_text.blank? + + timestamp = Time.zone.now.strftime("%Y-%m-%dT%H:%M:%S") + group_name = @group.name.gsub(/[^\w]+/, "") + file_name = "license_#{group_name}_#{timestamp}.txt" + license = @group.register_custom_license(license_text, current_user, file_name) + current_user.signs_license_for_project(license, @group, model='group') + + flash[:notice] = 'A license is added. You can force users to sign multiple license agreements if needed.' + redirect_to :action => :show + end + + def show_license #:nodoc: + # @group = @current_user.visible_groups.where(id: params[id]).first + + unsigned_licenses = current_user.unsigned_custom_licenses(@group) + + if unsigned_licenses.empty? + if @current_licenses.present? + flash[:notice] = 'You already signed all licenses' + else + flash[:notice] = 'No licenses are defined for this project' + redirect_to :action => :show + return + end + end + + # What to show. If a license is given in params, + # we make sure it's a registered one and we pick that. + @license_id = false unless @current_licenses.include? @license_id + # If no valid license was given and there are unsigned licenses, pick the first + @license_id ||= unsigned_licenses.first.try(:to_i) + # Otherwise, show the first license. + @license_id ||= @current_licenses.first + + # Load the text of the license + userfile = Userfile.find(@license_id) + userfile.sync_to_cache + @license_text = userfile.cache_readhandle { |fh| fh.read } + + # Identifies if the current user has already signed it, + # and whether they are the author + end + + def sign_license #:nodoc: + + unless @group.custom_license_agreements.include?(@license_id) + flash[:error] = 'You are trying to access unrelated license. Try again or report the issue to the support.' + redirect_to :action => :show + return + end + + if @is_signed + flash[:error] = 'You have already signed this license.' + redirect_to :action => :show + return + end + + unless params.has_key?(:agree) + flash[:error] = "You cannot access that project without signing the License Agreement first." + redirect_to :action => :index + return + end + + if params[:license_check].blank? || params[:license_check].to_i == 0 + flash[:error] = "There was a problem with your submission. Please read the agreement and check the checkbox." + redirect_to show_license_group_path(@group) + return + end + + license = Userfile.find(@license_id) + current_user.signs_license_for_project(license, @group, model='group') + + if current_user.unsigned_custom_licenses(@group).empty? + flash[:notice] = 'You signed all the project licenses' + else + flash[:notice] = 'This project has at least one other license agreement' + end + redirect_to :action => :show, :id => @group.id + end + + # check license for project (group) with id pid, + def license_check(pid=false) + pid = pid || params[:id] + + + return true if pid == 'all' ## to do && current_user.has_role(:admin) + # if unexpected id - let the action method handle the error message + begin + @group = current_user.viewable_groups.find(pid) + rescue ActiveRecord::RecordNotFound + return true + end + if current_user.unsigned_custom_licenses(@group).present? + flash[:error] = "Access to the project #{@group.name} is blocked due to licensing issues. Please sign the license" + # license_redirect + raise CbrainLicenseException + end + end + private def group_params #:nodoc: @@ -285,19 +396,14 @@ def group_params #:nodoc: end end - def license_check - return true if params[:id].blank? - return true if params[:id] == 'all' - # if unexpected id - let the action method handle the error message - begin - @group = current_user.viewable_groups.find(params[:id]) - rescue ActiveRecord::RecordNotFound - return true - end - if current_user.unsigned_custom_licenses(@group).present? - flash[:error] = "Access to the project #{@group.name} is blocked due to licensing issues. Please consult with the project maintainer or support for details" - license_redirect - end + def group_license_attributes # helper updates custom license attribute + @group = @current_user.viewable_groups.find(params[:id]) + @can_add_license = current_user.id == @group&.creator_id + @current_licenses = @group.custom_license_agreements + + param_lic_id = params['license_id'].presence + @license_id = param_lic_id && param_lic_id.to_i # nil if param is nil-like, otherwise cast to integer + @is_signed = current_user.custom_licenses_signed.include?(@license_id) end end diff --git a/BrainPortal/app/controllers/nh_projects_controller.rb b/BrainPortal/app/controllers/nh_projects_controller.rb index 79df20493..4b267b785 100644 --- a/BrainPortal/app/controllers/nh_projects_controller.rb +++ b/BrainPortal/app/controllers/nh_projects_controller.rb @@ -163,7 +163,7 @@ def add_license #:nodoc: group_name = @nh_project.name.gsub(/[^\w]+/,"") file_name = "license_#{group_name}_#{timestamp}.txt" license = @nh_project.register_custom_license(license_text, current_user, file_name) - user_signs_license_for_project(current_user, license, @nh_project) + current_user.signs_license_for_project(license, @nh_project) flash[:notice] = 'A license is added. You can force users to sign multiple license agreements if needed.' redirect_to :action => :show @@ -236,7 +236,7 @@ def sign_license #:nodoc: end license = Userfile.find(@license_id) - user_signs_license_for_project(current_user, license, @nh_project) + current_user.signs_license_for_project(license, @nh_project) if current_user.unsigned_custom_licenses(@nh_project).empty? flash[:notice] = 'You signed all the project licenses' @@ -332,14 +332,5 @@ def redirect_show_license end end - # Records that +user+ signed the +license+ file for +project+ - # with nice log messages to that effect. - def user_signs_license_for_project(user, license, project) - user.add_signed_custom_license(license) - - user.addlog("Signed custom license agreement '#{license.name}' (ID #{license.id}) for project '#{project.name}' (ID #{project.id}).") - project.addlog("User #{user.login} signed license agreement '#{license.name}' (ID #{license.id}).") - end - end diff --git a/BrainPortal/app/models/user.rb b/BrainPortal/app/models/user.rb index c06063e17..a7b820595 100644 --- a/BrainPortal/app/models/user.rb +++ b/BrainPortal/app/models/user.rb @@ -203,8 +203,6 @@ def custom_licenses_signed=(licenses) #:nodoc: self.meta[:custom_licenses_signed] = Array(licenses) end - # Records that a custom license agreement has - # been signed by adding it to the list of signed ones. def add_signed_custom_license(license_file) cb_error "A license file is supposed to be a TextFile" unless license_file.is_a?(TextFile) signed = self.custom_licenses_signed @@ -213,6 +211,15 @@ def add_signed_custom_license(license_file) self.custom_licenses_signed = signed end + # Records that +user+ signed the +license+ file for +project+ + # with nice log messages to that effect. When signed on CBRAIN model should be 'group' and 'project' on NeuroHub's + def signs_license_for_project(license, project, model='project') + self.add_signed_custom_license(license) + self.addlog("Signed custom license agreement '#{license.name}' (ID #{license.id}) for #{model} '#{project.name}' (ID #{project.id}).") + project.addlog("User #{self.login} signed license agreement '#{license.name}' (ID #{license.id}).") + end + + ############################################### # # Password and login gestion diff --git a/BrainPortal/app/views/groups/_show_license_text.html.erb b/BrainPortal/app/views/groups/_show_license_text.html.erb new file mode 100644 index 000000000..ac546dccf --- /dev/null +++ b/BrainPortal/app/views/groups/_show_license_text.html.erb @@ -0,0 +1,42 @@ + +<%- + # + # NeuroHub Project + # + # Copyright (C) 2020 + # The Royal Institution for the Advancement of Learning + # McGill University + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU General Public License as published by + # the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program 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 General Public License for more details. + # + # You should have received a copy of the GNU General Public License + # along with this program. If not, see . + # +-%> + +
+

Carefully read this license agreement.

+
<%= @license_text %>
+ <% if @is_signed %> + <% if @is_author %> +
<%= nh_icon_checkmark %>As the author of the license you have implicitely signed this agreement.
+ <% else %> +
<%= nh_icon_checkmark %>You have already signed this license agreement.
+ <% end %> + <% end %> + <% if !@is_signed %> +
<%= nh_icon_error %> This license agreement is not yet signed.
+
+ <%= check_box_tag :license_check, 1, nil, :class => "my-2" %> + <%= label_tag(:license_check,"By clicking here, I agree with all the conditions above.", :class=>"ml-2 card-label") %> +
+ <% end %> +
diff --git a/BrainPortal/app/views/groups/new_license.html.erb b/BrainPortal/app/views/groups/new_license.html.erb new file mode 100644 index 000000000..bd7f549eb --- /dev/null +++ b/BrainPortal/app/views/groups/new_license.html.erb @@ -0,0 +1,53 @@ + +<%- +# +# CBRAIN Project +# +# Copyright (C) 2020 +# The Royal Institution for the Advancement of Learning +# McGill University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +-%> + +<% title "Add License", "" %> + +

Add New Licence to Project <%= @group.name %>

+ +
+ <%= form_tag add_license_group_path(@group) do %> +
+
Important: + All members of the project will be required to sign the agreement in order to access the project. + You, as the author, will be be considered as having signed it implicitly. +
+ +

+ +

+

+ <%= text_area_tag "license_text", "", :required => true, :rows => 20, :class => "whole_width" %> + +

+
+ <%= submit_tag "Add License", :name => :add_license, :class => "button" %> + + You can assign additional license agreements at any time. + +
+
+ <% end %> +
+ diff --git a/BrainPortal/app/views/groups/show.html.erb b/BrainPortal/app/views/groups/show.html.erb index 6843e2c0e..d8181b079 100644 --- a/BrainPortal/app/views/groups/show.html.erb +++ b/BrainPortal/app/views/groups/show.html.erb @@ -37,7 +37,12 @@ <% end %> <%= link_to 'Delete', group_path(@group), { :data => { :confirm => "Are you sure you want to delete '#{@group.name}'?" }, :method => :delete, :class => "button" } %> <% end %> - + <% if @can_add_license %> + <%= link_to "Add License", new_license_group_path(@group.id), :class => "button" %> + <% end %> + + + <% end %>

@@ -165,6 +170,28 @@ <% end %> <% end %> + + + + + <% if @current_licenses.size > 0 %> + + <% default_text = array_to_table(@current_licenses, :table_class => 'simple bordered float_left', :cols => 20, :min_data => 20, :fill_by_columns => true ) {|id, r, c| + link_to "License ##{id}", show_license_group_path(:group_id => @group.id, :license_id => id) + } %> + + + <%= show_table(@group, :as => :group, :header => 'Project License Agreements', :edit_condition => false) do |t| %> + <% t.edit_cell(:licenses, :show_width => 2, + :header => 'Licenses', + :content => default_text) do %> + + <% end %> + <% end %> + + <% end %> + + <% if @group.can_be_edited_by?(current_user) %> @@ -172,3 +199,5 @@ <%= render :partial => "layouts/log_report", :locals => { :log => @group.getlog, :title => 'Project Log' } %> <% end %> + + diff --git a/BrainPortal/app/views/groups/show_license.html.erb b/BrainPortal/app/views/groups/show_license.html.erb new file mode 100644 index 000000000..0b5e03b4f --- /dev/null +++ b/BrainPortal/app/views/groups/show_license.html.erb @@ -0,0 +1,78 @@ + +<%- +# +# CBRAIN Project +# +# Copyright (C) 2020 +# The Royal Institution for the Advancement of Learning +# McGill University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +-%> + +<% title("License #{@license_id}", '') %> + +

License <%= @license_id %>

+ +
+ + <% unless @is_signed %> + The owner of the project <%= @group.name %> forbade access to the project info, until you sign the license + <% else %> + You already signed this license + <% end %> + + <%- # actions -%> + <% if @can_add_license %> + <%= link_to "Add License", new_license_group_path(@group.id) %> + <% end %> + +

+ + <%#= render :partial => 'show_license_text' %> + +

+ <%= form_tag sign_license_group_path(@group) do %> + <%= hidden_field_tag :license_id, @license_id %> + +
<%= @license_text %>
+ + <% unless @is_signed %> +

+ <%= check_box_tag :license_check, 1, nil, :class => "my-2" %> + By clicking here, I agree with all the conditions above. +

+

+ <%= submit_tag "I agree with the above statement", :name => :agree, :class => "button" %> + <%= submit_tag "I do not agree", :name => :not_agree, :class => "button" %> +

+ <% end %> + <% end %> +
+

+ + <% if @current_licenses.size > 1 %> +

+ Licenses for the project <%= @group.name %> +

+
    + <% @current_licenses.each do |id| %> +
  • + <%= link_to "License ##{id} #{id == @license_id? "- currently selected": ""}", show_license_group_path(:group_id => @group.id, :license_id => id), 'data-active' => (id == @license_id), 'disabled' => (id == @license_id) %> +
  • + <% end %> +
+ <% end %> +
diff --git a/BrainPortal/app/views/nh_projects/new_license.html.erb b/BrainPortal/app/views/nh_projects/new_license.html.erb index 67e19298c..aef9f7a21 100644 --- a/BrainPortal/app/views/nh_projects/new_license.html.erb +++ b/BrainPortal/app/views/nh_projects/new_license.html.erb @@ -29,7 +29,7 @@ <%= form_tag add_license_nh_project_path(@nh_project) do %>
Add License -

Important: All members of the project will be required to sign the agreement in order to access the project. You, as the author, will be be considered as having signed it implicitly.

+

Important: All members of the project will be required to sign the agreement in order to access the project. You, as the author, will be considered as having signed it implicitly.

diff --git a/BrainPortal/config/routes.rb b/BrainPortal/config/routes.rb index 9b0605940..ea4f3a81d 100644 --- a/BrainPortal/config/routes.rb +++ b/BrainPortal/config/routes.rb @@ -74,6 +74,12 @@ end resources :groups, :except => [ :edit ] do + member do + get :new_license + post :add_license + get :show_license + post :sign_license + end collection do post 'unregister' post 'switch'