fiedl/your_platform

View on GitHub
app/models/ability.rb

Summary

Maintainability
F
3 days
Test Coverage
class Ability
  include CanCan::Ability

  def ability_classes
    [
      #Abilities::CorporationAbility,
      #Abilities::GroupAbility,
      #Abilities::UserAbility,
      #Abilities::UserAccountAbility,
      #Abilities::ProfileFieldAbility,
      #Abilities::DummyAbility
      Abilities::FreeGroupAbility,
      Abilities::PostAbility,
      Abilities::AttachmentAbility
    ]
  end

  # def initialize(user, options = {})
  #   ability_classes.each do |ability_class|
  #     self.merge ability_class.new(user, options.merge(parent_ability: self))
  #   end
  # end

  def initialize(user, options = {})
    # Define abilities for the passed in user here. For example:
    #
    #   user ||= User.new # guest user (not logged in)
    #   if user.admin?
    #     can :manage, :all
    #   else
    #     can :read, :all
    #   end
    #
    # The first argument to `can` is the action you are giving the user
    # permission to do.
    # If you pass :manage it will apply to every action. Other common actions
    # here are :read, :create, :update and :destroy.
    #
    # The second argument is the resource the user can perform the action on.
    # If you pass :all it will apply to every resource. Otherwise pass a Ruby
    # class of the resource.
    #
    # The third argument is an optional hash of conditions to further filter the
    # objects.
    # For example, here the user can only update published articles.
    #
    #   can :update, Article, :published => true
    #
    # See the wiki for details:
    # https://github.com/ryanb/cancan/wiki/Defining-Abilities

    # This is the new ability structure.
    # TODO: Migrate the other abilities to separated ability classes.
    #
    ability_classes.each do |ability_class|
      self.merge ability_class.new(user, options.merge(parent_ability: self))
    end

    # Attention: Check outside whether the user's role allowes that preview!
    # Currently, this is done in ApplicationController#current_ability.
    #
    @preview_as = options[:preview_as]
    @preview_as = nil if @preview_as.blank?

    # These roles do not make sense here and may block the `view_as?` method.
    @preview_as = nil if @preview_as.in? ['full_member']

    # There are two kinds of token: `User#token` and `AuthToken#token`,
    # which are both handled by the same parameter:
    @token = options[:token]
    @user_by_auth_token = options[:user_by_auth_token]

    @read_only_mode = options[:read_only_mode]
    @user = user || @user_by_auth_token

    # Fast lane
    can :read, Pages::PublicPage

    if @user.try(:account) # has to be able to sign in
      if @user_by_auth_token
        # The user is identified via auth token.
        # So, do not grant the regular rights but only
        # the rights granted by the token.
        rights_for_auth_token_users
      elsif user
        if user.has_flag? :dummy
          rights_for_dummy_users
        else
          if user.global_admin? and view_as?(:global_admin)
            rights_for_global_admins
          end
          if user.admin_of_anything? and view_as?(:admin)
            rights_for_local_admins
            rights_for_page_admins
          end
          if view_as?([:officer, :admin])
            rights_for_local_officers
          end
          if view_as?([:global_officer, :officer, :admin]) and user.is_global_officer?
            rights_for_global_officers
          end
          if user.developer?
            rights_for_developers
          end
          if user.beta_tester?
            rights_for_beta_testers
          end
          rights_for_signed_in_users
        end
      end
    end
    rights_for_everyone
    restrictions_for_everyone
  end

  def view_as?(role)
    (view_as.nil?) or (view_as.to_s == '') or if role.kind_of?(Array)
      view_as.in? role
    else
      view_as == role
    end
  end
  def view_as
    preview_as.try(:to_sym)
  end
  def preview_as
    @preview_as
  end
  def token
    @token
  end
  def auth_token
    @auth_token ||= AuthToken.where(token: token).first if @user_by_auth_token
  end
  def read_only_mode?
    @read_only_mode
  end
  def user
    @user
  end

  def rights_for_beta_testers
    # can :use, :new_menu_feature
    can :use, :tab_view  # this switch is only for user-tabs; group-tabs are for all.
    can :use, :merit
    can :use, :documents_index
    can :use, :nav_dropdowns
  end

  def rights_for_developers
    can :use, Rack::MiniProfiler

    can :use, :requests_index
    can :use, :permalinks
    can :use, :demo_mode

    can :use, :masquerade do
      # Only global admins that are developers are allowed
      # to masquerade as other users. This is used for debugging.
      user.global_admin?
    end
    can :masquerade_as, User do
      can? :use, :masquerade
    end
  end

  def rights_for_global_admins
    if read_only_mode?
      can :read, :all
      can :index, :all
    else
      can :manage, :all
      can :execute, [Workflow, WorkflowKit::Workflow]
      cannot :destroy, Group
    end

    # There are features for developers and beta testers.
    cannot :use, :all

    can :use, :demo_mode
  end

  def rights_for_local_admins
    # Achtung Wingolfsplattform: Überschreibt diese Methode derzeit!
    can :index, PublicActivity::Activity
    can :index, Issue
    if not read_only_mode?
      can :manage, Group do |group|
        group.admins_of_self_and_ancestors.include? user
      end

      can [:update, :change_first_name, :change_alias, :create_account_for, :change_status], User, id: Role.of(user).administrated_users.map(&:id)
      can :manage, UserAccount, user_id: Role.of(user).administrated_users.map(&:id)

      can :manage, ProfileField do |profile_field|
        profile_field.profileable.nil? ||  # in order to create profile fields
          can?(:update, profile_field.profileable)
      end

      can :execute, [Workflow, WorkflowKit::Workflow] do |workflow|
        # Local admins can execute workflows of groups they're admins of.
        # And they can execute the mark_as_deceased workflow, which is a global workflow.
        #
        (workflow.id == Workflow.find_mark_as_deceased_workflow_id) ||
        (workflow.admins_of_ancestors.include?(user))
      end
    end

    can :index, :home_pages
    can :create, Pages::HomePage
  end

  def rights_for_page_admins
    can :manage, Page do |page|
      user.administrated_objects.include? page
    end
    can :update, Event do |event|
      event.id.in? Page.where(id: user.page_ids_of_pages_the_user_is_officer_of).collect(&:event_ids).flatten
    end
  end

  def rights_for_local_officers
    can :export_member_list, Group do |group|
      user.in? group.officers_of_self_and_ancestors
    end

    # Who can read bank-account information of users?
    # - [x] The user herself --> `rights_for_signed_in_users`
    # - [x] Kassenwarte des eigenen PhV/BV/WV
    # - [x] Wohnheimsverein-Kassenwarte, wenn Hausbewohner
    #
    can :read_bank_account, User do |other_user|
      (user.groups.flagged(:kassenwart).map(&:scope) & other_user.groups).any? ||
      (other_user.groups.where(type: "Groups::Room").map(&:kassenwarte).flatten.include? user)
    end

    if not read_only_mode?
      # Local officers can create events in their groups.
      #
      can :create, Event
      can [:create_event, :create_event_for], Group do |group|
        user.in? group.officers_of_self_and_ancestor_groups
      end
      can [:update, :destroy, :invite_to], Event do |event|
        event.group && user.in?(event.group.officers_of_self_and_ancestor_groups)
      end
      can :update, Group do |group|
        group.has_flag?(:contact_people) && can?(:update, group.parent_events.first)
      end
      can :export_calendar_feed_url_for_local_homepage, Group do |group|
        can? :update, group
      end

      can :update, SemesterCalendar do |semester_calendar|
        semester_calendar.group && user.in?(semester_calendar.group.officers_of_self_and_ancestor_groups)
      end
      can :create_attachment_for, SemesterCalendar do |semester_calendar|
        can? :update, semester_calendar
      end
      can :change_term, SemesterCalendar do |semester_calendar|
        can?(:update, semester_calendar)
      end
      can :create, SemesterCalendar
      can :destroy, SemesterCalendar do |semester_calendar|
        can? :update, semester_calendar
      end

      can :update_accommodations, Corporation do |corporation|
        user.in? corporation.officers
      end
      can :create_accommodation_sepa_debit, Corporation do |corporation|
        user.in? corporation.wohnheimsverein.officers if corporation.wohnheimsverein
      end

      # Local officers of pages can edit their pages and sub-pages
      # as long as they are the authors or the pages have *no* author.
      #
      can [:update, :destroy, :publish], Page do |page|
        can?(:read, page) and page.officers_of_self_and_ancestors.include?(user) and (page.author == user or page.author.nil?)
      end

      # Create, update and destroy Pages
      #
      can :create_page_for, [Group, Page] do |parent|
        parent.officers_of_self_and_ancestors.include?(user)
      end
      can [:update, :destroy, :publish], Page do |page|
        (page.author == user) && (page.group) && (page.group.officers_of_self_and_ancestors.include?(user))
      end

      # Create, update and destroy Attachments
      #
      can :create_attachment_for, Page do |page|
        (page.group) && (page.group.officers_of_self_and_ancestors.include?(user))
      end

      # Local officers of pages can add attachments to the page and subpages
      # and modify their own attachments.
      #
      can :create_attachment_for, Page do |page|
        can?(:read, page) and page.officers_of_self_and_ancestors.include?(user)
      end

    end
  end

  def rights_for_global_officers
    can :export_member_list, Group
    if not read_only_mode?
      can [:create_event, :create_event_for], Group
      can :create, Event
      can [:update, :destroy, :invite_to], Event do |event|
        event.contact_people.include? user
      end
    end
  end

  def rights_for_signed_in_users
    can :read, :terms_of_use
    can :accept, :terms_of_use if not read_only_mode?
    can :use, :single_sign_on

    can :index, :root

    can :index, :contacts
    can :index, :documents
    can :index, :my_groups
    can :index, :gesuche_und_angebote
    can :index, :calendars
    can :index, :heraldics
    can :index, SupportRequest

    can :index_mine, Group

    can :read, User, id: user.id
    can :index, User

    can :read, Corporation
    can :index_accommodations, Corporation

    # Regular users can access the list of mailing lists.
    #
    can :index, MailingList

    # Regular users can index officers.
    can :index, :officers

    if not read_only_mode?
      # Jeder muss Dokumente erstellen können, z.B. um Konventsprotokolle hochzuladen.
      # Ansonsten würden auch Verteilermails mit Dokumenten fehlschlagen.
      can :create, Document

      # Regular users can create, update or destroy own profile fields
      # that do not belong to the General section.
      #
      can [:create, :read, :update, :destroy], ProfileField do |field|
        field.profileable.nil? or # to allow creating fields
        ((field.profileable == user) and (field.type != 'ProfileFields::General') and (field.key != 'date_of_birth'))
      end

      # Regular users can update their personal title and academic degree.
      #
      can :update, ProfileField do |field|
        (field.profileable == user) and (field.key.in? ['personal_title', 'academic_degree', 'cognomen'])
      end

      # They can change their first name, but not their surname.
      #
      can [:update, :change_first_name, :change_alias], User, :id => user.id

      # They can change their password, i.e. modify their user account.
      #
      can :update, UserAccount, :user_id => user.id

      # Regular users can update their own validity ranges of memberships
      # in order to update their corporate vita.
      #
      can :update, Membership, :descendant_id => user.id

      # Everyone who can join an event, can add images to this event.
      # Then, he will automatically join the event.
      # Also, all contact people can add images.
      #
      can :create_attachment_for, Event do |event|
        can?(:join, event) or event.attendees.include?(user) or event.contact_people.include?(user)
      end

      # If a user is contact person of an event, he can provide pages and
      # attachment for this event.
      #
      # TODO: New role 'contact person'.
      #
      can [:update, :create_page_for], Event do |event|
        event.contact_people.include? user
      end
      can [:update, :create_attachment_for, :destroy, :publish], Page do |page|
        page.ancestor_events.map(&:contact_people).flatten.include? user
      end

      # If a user can read an object, he can comment it.
      #
      can [:create_comment_for, :create_comment], [Post] do |commentable|
        can? :read, commentable
      end

      # There are blogs where everyone can post.
      #
      can :create_page_for, Page do |page|
        page.settings.anyone_can_create_blog_posts && can?(:read, page)
      end
      can [:update, :destroy, :publish], Page do |page|
        page.parent.kind_of?(Page) && page.parent.settings.anyone_can_create_blog_posts && can?(:read, page) && (page.author == user)
      end

    end

    can :read_bank_account, User, :id => user.id

    can :read, Group do |group|
      # Regular users cannot see the former_members_parent groups
      # and their descendant groups.
      #
      not (group.has_flag?(:former_members_parent) || group.ancestor_groups.find_all_by_flag(:former_members_parent).count > 0)
    end

    can :read, Page do |page|
      (page.group.nil? || page.group.members.include?(user)) && page.ancestor_users.none?
    end

    # All users can join events.
    #
    can :read, Event
    if not read_only_mode?
      can :join, Event
      can :leave, Event
    end
    can :index, Event
    can :index_events, Group
    can :index_events, User, :id => user.id

    can [:create_post, :create_post_for], Event, group_id: user.group_ids
    can [:create_post, :create_post_for], Event, parent_groups: { id: user.group_ids }

    # Name auto completion
    #
    can :autocomplete_title, User

    # All users can read their notifications.
    can :index, Notification
    can :read, Notification, recipient: {id: user.id}

    # Comments:
    # - Users can read comments for all objects they can read anyway.
    # - And they can create comments for these objects as well (see above.)
    can :read, Comment do |comment|
      can? :read, comment.commentable
    end

    # All signed-in users can read their news (timeline).
    can :index, :news

    # Read projects
    can [:read, :update], Project do |project|
      project.group.members.include? user
    end

    # Renew caches
    can :renew_cache, [User, Group, Page, Event] do |obj|
      can? :read, obj
    end

    # Betas
    # All users can see what betas we have.
    can :read, Beta
    can :index, Beta
    # Only beta members can create invitations.
    can :create_beta_invitation_for, Beta do |beta|
      beta.invitees.include? user
    end
  end

  def rights_for_auth_token_users
    p "===TOKEN #{auth_token.user} #{auth_token.resource}"
    if auth_token && Ability.new(auth_token.user).can?(:read, auth_token.resource)
      can :read, auth_token.resource

      if auth_token.resource.kind_of? Page
        auth_token.resource.attachments.each do |attachment|
          can [:read, :download], attachment
        end
      elsif auth_token.resource.kind_of? Event
        auth_token.resource.attachments.each do |attachment|
          can [:read, :download], attachment
        end
        # TODO  can post photos
      end
    end
  end

  def rights_for_everyone

    # Feature switches
    #
    can :use, :semester_calendars

    # can :use, :mail_delivery_account_filter
    can :use, :help_videos
    can :use, :permalinks

    # it's not ready, yet, but the tests use it already.
    if Rails.env.test?
      can :use, :wysihtml

      can :use, :blog_post_comments
      can :use, :mailing_lists
    end

    # Use the tabs view in users#show. This has been a beta feature previously.
    # TODO: Remove the feature switch whenever you feel we won't switch back.
    #
    can :use, :tab_view


    # Imprint
    # Make sure all users (even if not logged in) can read the imprint.
    #
    can :read, Page, Page.flagged(:imprint) do |page|
      # This block is to make sure that the scope scope worked.
      # FIXME: Make this block unnecessary.
      page.has_flag?(:imprint)
    end

    # All users can read the public website.
    #
    can :read, Page do |page|
      page.public?
    end

    # All users can read the public bios of the users.
    #
    can :read_public_bio, User

    # All users can comment contents they can read.
    #
    can :create_comment_for, BlogPost do |blog_post|
      can? :read, blog_post
    end

    # All users can read public events.
    #
    can :read, Event do |event|
      event.publish_on_global_website || event.publish_on_local_website
    end
    can :index_attendees, Event do |event|
      can? :join, event
    end

    # All users can read semester calendars.
    can :index_events, Group
    can :read, SemesterCalendar

    # Listing Events and iCalendar (ICS) Export:
    #
    # There are event lists on public websites and webcal feeds.
    # Therefore the user might not be logged in through a regular
    # session. Some public feeds can be seen by anyone. Other
    # feeds require an auth token.
    #
    if token.present?
      tokened_user = UserAccount.find_by_auth_token(token)
      if tokened_user
        can :index_events, Group
        can :index_events, User do |other_user|
          # To index the events relevant to a certain user,
          # one has to provide the correct auth token that corresponds
          # to that user.
          tokened_user == other_user.account
        end
        can :read, Event do |event|
          tokened_user.user.can? :read, event
        end
      end
    end
    can :index_public_events, :all

    # RSS Feeds
    can :index, :feeds
    can :read, :default_feed
    can :read, :public_feed
    if token.present? # && tokened_user = UserAccount.find_by_auth_token(token).try(:user)
      can :read, :personal_feed
    end

    # All users can show tags. This does not mean that they
    # can see all tagged contents though.
    can :read, ActsAsTaggableOn::Tag

    # Activate platform mailgate, i.e. accept incoming email.
    # The authorization to send to a specific group is done separately in
    # the StoreMailAsPostsAndSendGroupMailJob.
    #
    can :use, :platform_mailgate

    # All users can use the blue help button.
    #
    can :create, :support_request

    # All users can use the contact form.
    #
    can :create, ContactMessage

    # All users can load avatar images, since this is an api.
    # See AvatarsController.
    #
    can :read, :avatars

  end

  # During App-Store approval, we must give a dummy user access to our platform.
  # But he should not be able to access any real data.
  #
  def rights_for_dummy_users
    can :read, :terms_of_use
    can :accept, :terms_of_use if not read_only_mode?

    can :read, User do |user|  # Only read other dummy users.
      user.has_flag? :dummy
    end

    can :read, Corporation do |corporation|
      corporation.active?
    end

    can :index, Event
    can :index, User
    can :index, Corporation

    can :read, Page do |page|
      page.has_flag? :songs
    end

    can [:read, :download], Attachment do |document|
      document.has_flag? :dummy
    end
  end

  def restrictions_for_everyone
    cannot :use, :pdf_search  # It's not ready, yet. https://trello.com/c/aYtvpSij/1057

    # Nobody can destroy non-empty pages that are older than 10 minutes.
    # Pages that are no longer needed can be archived instead.
    # Public pages can be destroyed until TODO we have the archive-pages interface back.
    #
    cannot :destroy, Page do |page|
      (page.created_at < 10.minutes.ago) && page.not_empty? && !page.kind_of?(Pages::PublicPage)
    end

    # Nobody can destroy events with attendees.
    # If an event is cancelled, it should be noted in the event title.
    # Otherwise, the event would just be missing in the users' calendars.
    #
    cannot :destroy, Event do |event|
      event.attendees.count > 0
    end

    # Nobody can destroy semester calendars with attachments.
    #
    cannot :destroy, SemesterCalendar do |semester_calendar|
      semester_calendar.attachments.count > 0
    end

    # Nobody can destroy or update a ballot that is signed.
    cannot [:update, :destroy, :submit, :create_attachment_for], DecisionMaking::Ballot do |ballot|
      ballot.signed?
    end

    # We don't want people to re-use older semester calendars. Thus, after a while,
    # the term cannot be changed anymore. They should create new calendars for new
    # terms.
    cannot :change_term, SemesterCalendar do |semester_calendar|
      semester_calendar.created_at < 1.day.ago
    end

    # Nobody can recalculate term reports that have already been submitted.
    cannot :recalculate, TermReport do |term_report|
      term_report.state && !(term_report.state.rejected?)
    end
  end

end