]> BookStack Code Mirror - bookstack/blob - app/Http/Controllers/Auth/Saml2Controller.php
Applied styleci changes
[bookstack] / app / Http / Controllers / Auth / Saml2Controller.php
1 <?php
2
3 namespace BookStack\Http\Controllers\Auth;
4
5 use BookStack\Auth\Access\Saml2Service;
6 use BookStack\Http\Controllers\Controller;
7 use Illuminate\Http\Request;
8 use Illuminate\Support\Facades\Cache;
9 use Str;
10
11 class Saml2Controller extends Controller
12 {
13     protected $samlService;
14
15     /**
16      * Saml2Controller constructor.
17      */
18     public function __construct(Saml2Service $samlService)
19     {
20         $this->samlService = $samlService;
21         $this->middleware('guard:saml2');
22     }
23
24     /**
25      * Start the login flow via SAML2.
26      */
27     public function login()
28     {
29         $loginDetails = $this->samlService->login();
30         session()->flash('saml2_request_id', $loginDetails['id']);
31
32         return redirect($loginDetails['url']);
33     }
34
35     /**
36      * Start the logout flow via SAML2.
37      */
38     public function logout()
39     {
40         $logoutDetails = $this->samlService->logout();
41
42         if ($logoutDetails['id']) {
43             session()->flash('saml2_logout_request_id', $logoutDetails['id']);
44         }
45
46         return redirect($logoutDetails['url']);
47     }
48
49     /*
50      * Get the metadata for this SAML2 service provider.
51      */
52     public function metadata()
53     {
54         $metaData = $this->samlService->metadata();
55
56         return response()->make($metaData, 200, [
57             'Content-Type' => 'text/xml',
58         ]);
59     }
60
61     /**
62      * Single logout service.
63      * Handle logout requests and responses.
64      */
65     public function sls()
66     {
67         $requestId = session()->pull('saml2_logout_request_id', null);
68         $redirect = $this->samlService->processSlsResponse($requestId) ?? '/';
69
70         return redirect($redirect);
71     }
72
73     /**
74      * Assertion Consumer Service start URL. Takes the SAMLResponse from the IDP.
75      * Due to being an external POST request, we likely won't have context of the
76      * current user session due to lax cookies. To work around this we store the
77      * SAMLResponse data and redirect to the processAcs endpoint for the actual
78      * processing of the request with proper context of the user session.
79      */
80     public function startAcs(Request $request)
81     {
82         // Note: This is a bit of a hack to prevent a session being stored
83         // on the response of this request. Within Laravel7+ this could instead
84         // be done via removing the StartSession middleware from the route.
85         config()->set('session.driver', 'array');
86
87         $samlResponse = $request->get('SAMLResponse', null);
88
89         if (empty($samlResponse)) {
90             $this->showErrorNotification(trans('errors.saml_fail_authed', ['system' => config('saml2.name')]));
91
92             return redirect('/login');
93         }
94
95         $acsId = Str::random(16);
96         $cacheKey = 'saml2_acs:' . $acsId;
97         cache()->set($cacheKey, encrypt($samlResponse), 10);
98
99         return redirect()->guest('/saml2/acs?id=' . $acsId);
100     }
101
102     /**
103      * Assertion Consumer Service process endpoint.
104      * Processes the SAML response from the IDP with context of the current session.
105      * Takes the SAML request from the cache, added by the startAcs method above.
106      */
107     public function processAcs(Request $request)
108     {
109         $acsId = $request->get('id', null);
110         $cacheKey = 'saml2_acs:' . $acsId;
111         $samlResponse = null;
112
113         try {
114             $samlResponse = decrypt(cache()->pull($cacheKey));
115         } catch (\Exception $exception) {
116         }
117         $requestId = session()->pull('saml2_request_id', 'unset');
118
119         if (empty($acsId) || empty($samlResponse)) {
120             $this->showErrorNotification(trans('errors.saml_fail_authed', ['system' => config('saml2.name')]));
121
122             return redirect('/login');
123         }
124
125         $user = $this->samlService->processAcsResponse($requestId, $samlResponse);
126         if (is_null($user)) {
127             $this->showErrorNotification(trans('errors.saml_fail_authed', ['system' => config('saml2.name')]));
128
129             return redirect('/login');
130         }
131
132         return redirect()->intended();
133     }
134 }