This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Class SAMLController |
||
4 | * |
||
5 | * This controller handles serving metadata requests for the IdP, as well as handling |
||
6 | * creating new users and logging them into SilverStripe after being authenticated at the IdP. |
||
7 | */ |
||
8 | class SAMLController extends Controller |
||
0 ignored issues
–
show
|
|||
9 | { |
||
10 | /** |
||
11 | * @var array |
||
12 | */ |
||
13 | private static $allowed_actions = [ |
||
0 ignored issues
–
show
|
|||
14 | 'index', |
||
15 | 'login', |
||
16 | 'logout', |
||
17 | 'acs', |
||
18 | 'sls', |
||
19 | 'metadata' |
||
20 | ]; |
||
21 | |||
22 | /** |
||
23 | * Assertion Consumer Service |
||
24 | * |
||
25 | * The user gets sent back here after authenticating with the IdP, off-site. |
||
26 | * The earlier redirection to the IdP can be found in the SAMLAuthenticator::authenticate. |
||
27 | * |
||
28 | * After this handler completes, we end up with a rudimentary Member record (which will be created on-the-fly |
||
29 | * if not existent), with the user already logged in. Login triggers memberLoggedIn hooks, which allows |
||
30 | * LDAP side of this module to finish off loading Member data. |
||
31 | * |
||
32 | * @throws OneLogin_Saml2_Error |
||
33 | */ |
||
34 | public function acs() |
||
35 | { |
||
36 | $auth = Injector::inst()->get('SAMLHelper')->getSAMLAuth(); |
||
37 | $auth->processResponse(); |
||
38 | |||
39 | $error = $auth->getLastErrorReason(); |
||
40 | if (!empty($error)) { |
||
41 | SS_Log::log($error, SS_Log::ERR); |
||
42 | Form::messageForForm("SAMLLoginForm_LoginForm", "Authentication error: '{$error}'", 'bad'); |
||
43 | Session::save(); |
||
44 | return $this->getRedirect(); |
||
45 | } |
||
46 | |||
47 | if (!$auth->isAuthenticated()) { |
||
48 | Form::messageForForm("SAMLLoginForm_LoginForm", _t('Member.ERRORWRONGCRED'), 'bad'); |
||
49 | Session::save(); |
||
50 | return $this->getRedirect(); |
||
51 | } |
||
52 | |||
53 | $decodedNameId = base64_decode($auth->getNameId()); |
||
54 | // check that the NameID is a binary string (which signals that it is a guid |
||
55 | if (ctype_print($decodedNameId)) { |
||
56 | Form::messageForForm("SAMLLoginForm_LoginForm", "Name ID provided by IdP is not a binary GUID.", 'bad'); |
||
57 | Session::save(); |
||
58 | return $this->getRedirect(); |
||
59 | } |
||
60 | |||
61 | // transform the NameId to guid |
||
62 | $guid = LDAPUtil::bin_to_str_guid($decodedNameId); |
||
63 | if (!LDAPUtil::validGuid($guid)) { |
||
64 | $errorMessage = "Not a valid GUID '{$guid}' recieved from server."; |
||
65 | SS_Log::log($errorMessage, SS_Log::ERR); |
||
66 | Form::messageForForm("SAMLLoginForm_LoginForm", $errorMessage, 'bad'); |
||
67 | Session::save(); |
||
68 | return $this->getRedirect(); |
||
69 | } |
||
70 | |||
71 | // Write a rudimentary member with basic fields on every login, so that we at least have something |
||
72 | // if LDAP synchronisation fails. |
||
73 | $member = Member::get()->filter('GUID', $guid)->limit(1)->first(); |
||
74 | if (!($member && $member->exists())) { |
||
75 | $member = new Member(); |
||
76 | $member->GUID = $guid; |
||
77 | } |
||
78 | |||
79 | $attributes = $auth->getAttributes(); |
||
80 | |||
81 | foreach ($member->config()->claims_field_mappings as $claim => $field) { |
||
82 | if (!isset($attributes[$claim][0])) { |
||
83 | SS_Log::log( |
||
84 | sprintf( |
||
85 | 'Claim rule \'%s\' configured in LDAPMember.claims_field_mappings, but wasn\'t passed through. Please check IdP claim rules.', |
||
86 | $claim |
||
87 | ), SS_Log::WARN |
||
88 | ); |
||
89 | |||
90 | continue; |
||
91 | } |
||
92 | |||
93 | if(count($attributes[$claim]) > 1) { |
||
94 | $member->$field = $attributes[$claim]; |
||
95 | } else { |
||
96 | $member->$field = $attributes[$claim][0]; |
||
97 | } |
||
98 | } |
||
99 | |||
100 | $member->SAMLSessionIndex = $auth->getSessionIndex(); |
||
101 | |||
102 | // This will trigger LDAP update through LDAPMemberExtension::memberLoggedIn. |
||
103 | // The LDAP update will also write the Member record. We shouldn't write before |
||
104 | // calling this, as any onAfterWrite hooks that attempt to update LDAP won't |
||
105 | // have the Username field available yet for new Member records, and fail. |
||
106 | // Both SAML and LDAP identify Members by the GUID field. |
||
107 | $member->logIn(); |
||
108 | |||
109 | return $this->getRedirect(); |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * Generate this SP's metadata. This is needed for intialising the SP-IdP relationship. |
||
114 | * IdP is instructed to call us back here to establish the relationship. IdP may also be configured |
||
115 | * to hit this endpoint periodically during normal operation, to check the SP availability. |
||
116 | */ |
||
117 | public function metadata() |
||
118 | { |
||
119 | try { |
||
120 | $auth = Injector::inst()->get('SAMLHelper')->getSAMLAuth(); |
||
121 | $settings = $auth->getSettings(); |
||
122 | $metadata = $settings->getSPMetadata(); |
||
123 | $errors = $settings->validateMetadata($metadata); |
||
124 | if (empty($errors)) { |
||
125 | header('Content-Type: text/xml'); |
||
126 | echo $metadata; |
||
127 | } else { |
||
128 | throw new \OneLogin_Saml2_Error( |
||
129 | 'Invalid SP metadata: ' . implode(', ', $errors), |
||
130 | \OneLogin_Saml2_Error::METADATA_SP_INVALID |
||
131 | ); |
||
132 | } |
||
133 | } catch (Exception $e) { |
||
134 | SS_Log::log($e->getMessage(), SS_Log::ERR); |
||
135 | echo $e->getMessage(); |
||
136 | } |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * @return SS_HTTPResponse |
||
141 | */ |
||
142 | protected function getRedirect() |
||
143 | { |
||
144 | // Absolute redirection URLs may cause spoofing |
||
145 | View Code Duplication | if (Session::get('BackURL') && Director::is_site_url(Session::get('BackURL'))) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository.
Loading history...
|
|||
146 | return $this->redirect(Session::get('BackURL')); |
||
147 | } |
||
148 | |||
149 | // Spoofing attack, redirect to homepage instead of spoofing url |
||
150 | View Code Duplication | if (Session::get('BackURL') && !Director::is_site_url(Session::get('BackURL'))) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository.
Loading history...
|
|||
151 | return $this->redirect(Director::absoluteBaseURL()); |
||
152 | } |
||
153 | |||
154 | // If a default login dest has been set, redirect to that. |
||
155 | if (Security::config()->default_login_dest) { |
||
156 | return $this->redirect(Director::absoluteBaseURL() . Security::config()->default_login_dest); |
||
157 | } |
||
158 | |||
159 | // fallback to redirect back to home page |
||
160 | return $this->redirect(Director::absoluteBaseURL()); |
||
161 | } |
||
162 | } |
||
163 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.