Class: Panda::Core::Admin::SessionsController

Inherits:
BaseController
  • Object
show all
Defined in:
app/controllers/panda/core/admin/sessions_controller.rb

Instance Method Summary collapse

Methods inherited from BaseController

#add_breadcrumb, #authenticate_admin_user!, #authenticate_user!, #breadcrumbs, #current_user, #set_current_request_details, #user_signed_in?

Methods included from Panda::Core::Authorizable

#authorize!, #authorized_for?, #authorized_for_admin_access?, #can?

Instance Method Details

#createObject



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
# File 'app/controllers/panda/core/admin/sessions_controller.rb', line 24

def create
  auth = request.env["omniauth.auth"]
  provider_path = params[:provider]&.to_sym

  # Find the actual provider key (might be using path_name override)
  provider = find_provider_by_path(provider_path)

  resolved = Core.config.resolved_authentication_providers(request)
  unless provider && resolved.key?(provider)
    redirect_to , flash: {error: "Authentication provider not enabled"}
    return
  end

  user = User.find_or_create_from_auth_hash(auth)

  if user.persisted?
    # Per-login validation hook (e.g. email domain restrictions)
    if Core.config.authentication_validator
      validation_error = Core.config.authentication_validator.call(user, auth, request)
      if validation_error
        redirect_to , flash: {error: validation_error}
        return
      end
    end

    # Check if user is authorized to access the admin area.
    # Admin users always have access. Non-admin users are checked
    # via the configurable authorization_policy (e.g. role-based access
    # from panda-cms-pro).
    policy = Panda::Core.config.authorization_policy
    unless user.admin? || policy.call(user, :access_admin, nil)
      flash[:error] = "You do not have permission to access the admin area"
      flash.keep(:error) if Rails.env.test?
      redirect_to 
      return
    end

    session[Panda::Core::ADMIN_SESSION_KEY] = user.id
    Panda::Core::Current.user = user

    user.track_login!(request) if user.respond_to?(:track_login!)
    Panda::Core::UserActivity.log!(user: user, action: "login", request: request) if defined?(Panda::Core::UserActivity)

    ActiveSupport::Notifications.instrument("panda.core.user_login",
      user: user,
      provider: provider)

    # Consume stored redirect path once so it is cleared regardless of
    # which redirect branch is taken below.
    post_auth_path = session.delete(:post_auth_redirect_path)

    # Post-authentication redirect hook (e.g. workspace picker).
    # Wrapped in its own rescue so a broken hook falls through to the
    # default redirect rather than hitting the outer rescue (which
    # would redirect to login with the session already created).
    if (redirect_proc = Panda::Core.config.post_authentication_redirect)
      begin
        redirect_url = redirect_proc.call(user, request)
        if redirect_url.present?
          redirect_to append_admin_path_suffix(redirect_url, post_auth_path), allow_other_host: true, flash: {success: "Successfully logged in as #{user.name}"}
          return
        end
      rescue => e
        Rails.logger.error "post_authentication_redirect hook failed: #{e.class}: #{e.message}"
      end
    end

    # Redirect to originating subdomain if auth callback landed on root domain
    origin = session.delete(:origin_subdomain)
    if origin.present? && request.host.split(".").first != origin
      scheme = request.scheme
      base_domain = request.host.sub(/\A[^.]+\./, "")
      port = request.port
      port_suffix = [80, 443].include?(port) ? "" : ":#{port}"
      base_url = "#{scheme}://#{origin}.#{base_domain}#{port_suffix}#{Core.config.admin_path}"
      redirect_to append_admin_path_suffix(base_url, post_auth_path), allow_other_host: true, flash: {success: "Successfully logged in as #{user.name}"}
      return
    end

    # Use configured dashboard path or default to admin_root_path
    redirect_path = Panda::Core.config.dashboard_redirect_path || admin_root_path
    redirect_path = redirect_path.call if redirect_path.respond_to?(:call)
    redirect_to redirect_path, flash: {success: "Successfully logged in as #{user.name}"}
  else
    redirect_to , flash: {error: "Unable to create account: #{user.errors.full_messages.join(", ")}"}
  end
rescue => e
  Rails.logger.error "Authentication error: #{e.message}"
  redirect_to , flash: {error: "Authentication failed: #{e.message}"}
end

#destroyObject



125
126
127
128
129
130
131
132
# File 'app/controllers/panda/core/admin/sessions_controller.rb', line 125

def destroy
  session.delete(Panda::Core::ADMIN_SESSION_KEY)
  Panda::Core::Current.user = nil

  ActiveSupport::Notifications.instrument("panda.core.user_logout")

  redirect_to , flash: {success: "Successfully logged out"}
end

#failureObject



115
116
117
118
119
120
121
122
123
# File 'app/controllers/panda/core/admin/sessions_controller.rb', line 115

def failure
  message = params[:message] || "Authentication failed"
  strategy = params[:strategy] || "unknown"

  Rails.logger.error "OmniAuth failure: strategy=#{strategy}, message=#{message}"
  flash[:error] = "Authentication failed: #{message}"
  flash.keep(:error) if Rails.env.test?
  redirect_to 
end

#newObject



12
13
14
15
16
17
18
19
20
21
22
# File 'app/controllers/panda/core/admin/sessions_controller.rb', line 12

def new
  providers = Core.config.resolved_authentication_providers(request)
  @providers = providers.select do |key, config|
    # Developer provider doesn't need credentials, only shown in development
    if key.to_sym == :developer
      Rails.env.development?
    else
      config[:client_id].present? && config[:client_secret].present?
    end
  end.keys
end