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