aboutsummaryrefslogtreecommitdiff
path: root/app/controllers/welcome_controller.rb
blob: d5f5fe91bb0b47eeb9bb23034baddbef10f0a347 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# SPDX-FileCopyrightText: 2020 IN COMMON Collective <collective@incommon.cc>
#
# SPDX-License-Identifier: AGPL-3.0-or-later

class WelcomeController < ApplicationController
  # GET /
  def index
    @map       = Map.first
    @taxonomy  = @map&.taxonomy
    @resources = Resource.order(:uuid).page params[:page]
    Rails.logger.info "WECLOME ///// #{@resources&.count || 0}"
  end

  # GET /authenticate(/:token)
  # Discourse SSO Authentication
  def authenticate
    validate_token_format!

    # Try an ongoing SSO or create a new one
    @sso = SSO::FromDiscourse.new(token: params[:token], nonce: session[params[:token]])

    clear_current_session

    # Start SSO roundtrip if we're not passed a token
    if params[:token].nil?
      # Record this token and nonce in the session
      session[@sso.token] = @sso.nonce
      # Send to SSO authenticator
      redirect_to @sso.request_uri and return
    end

    # Validate authentication params from SSO
    begin
      @sso.parse(params)
    rescue ArgumentError => e
      Rails.logger.debug("SSO request failed: #{e.message}")
      return :forbidden
    end

    # Resolve SSO and finish authentication
    case @sso.status
    when :ok
      Rails.logger.info("Authentication succeeded!")
      find_or_create_current_user
      perform_background_jobs
      update_current_session
    when :unauthorized
      Rails.logger.info("Authentication failed!")
    end
    return :forbidden unless @current_user.present?

    # TODO add some memory of previously called URL and return there
    redirect_to '/my/dashboard'
  end

  # GET /dashboard
  def dashboard
    redirect_to '/authenticate' and return unless current_user.present?

    @stats = {
      counts: {
        resources: Resource.count,
        agents: Agent.count,
        categories: Category.count,
        sections: Section.count
      },
      current_agent: {
        name: current_agent.name,
        uuid: current_agent.uuid,
        counts: {
          resources: current_agent.resources&.count,
          taxonomies: current_agent.taxonomies&.count,
          categories: current_agent.categories&.count,
          sectiions: current_agent.sections&.count
        }
      },
      my_agents: current_user.agents.map { |a| { uuid: a.uuid, name: a.name } }
    }
  end

  # GET /logout
  def logout
    session.destroy
    render :index
  end

  private

  # Ensure nobody tries silly things with our session
  def validate_token_format!
    if params[:token].present? && !params[:token].match?(/[a-z0-9]{32}/)
      raise(ArgumentError, "Token invalid")
    end
  end

  # Remove any current session
  def clear_current_session
    Rails.logger.info("Removing current session (#{session[:current_user]})")
    session.destroy
    @current_user = nil
  end

  # Set @current_user to existing or new User record from SSO user info
  def find_or_create_current_user
    user_data = {
      external_id: @sso.user_info[:external_id],
      avatar_url: @sso.user_info[:avatar_url],
      email: @sso.user_info[:email],
      name: @sso.user_info[:name],
      username: @sso.user_info[:username]
    }

    @current_user = User.find_by(external_id: user_data[:external_id]) ||
                    begin
                      Rails.logger.info('new user...')
                      u = User.create(user_data)
                      Rails.logger.info('created user %s' % u.inspect)
                      u
                    rescue Exception => e
                      Rails.logger.warning("#{e.type}: #{e.message}")
                    end

    user_data.reverse_merge!(@current_user.attributes.symbolize_keys)
    @current_user.update(user_data) if user_data != @current_user.attributes
    @current_user
  end

  # Update user agents
  def perform_background_jobs
    if @current_user.present?
      EnsureAgentJob.perform_later(@current_user, @sso.user_info[:groups].split(','))
    end
  end

  # Save User ID and current agent in session
  def update_current_session
    if @current_user.present?
      session[:current_user]  = @current_user[:external_id]
      session[:current_agent] = current_agent_name
    end
  end
end