summaryrefslogtreecommitdiffstats
path: root/inc/shibauth.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'inc/shibauth.inc.php')
-rw-r--r--inc/shibauth.inc.php202
1 files changed, 202 insertions, 0 deletions
diff --git a/inc/shibauth.inc.php b/inc/shibauth.inc.php
new file mode 100644
index 0000000..6ae3a89
--- /dev/null
+++ b/inc/shibauth.inc.php
@@ -0,0 +1,202 @@
+<?php
+
+class ShibAuth
+{
+
+ /**
+ * Log user into master-server using the data provided by the current shibboleth session
+ * @param ?string $accessCode optional one-time access code to retreive session data via thrift
+ * @return array{status: string, firstName: string, lastName: string, mail: string, token: string, sessionId: string, userId: string, organizationId: string, url: string, error: string}
+ */
+ private static function loginInternal(?string $accessCode = null): array
+ {
+ if ($accessCode !== null) {
+ $entrop = strlen(count_chars($accessCode, 3));
+ if (strlen($accessCode) < 32 || strlen($accessCode) > 64 || $entrop < 10) {
+ return ['status' => 'error', 'error' => 'Malformed accessCode'];
+ }
+ }
+ // Handle
+ if (empty($_SERVER['persistent-id'])) {
+ // No persistent id given, should not happen!
+ file_put_contents('/tmp/shib-nopid-' . time() . '-' . $_SERVER['REMOTE_ADDR'] . '.txt', print_r($_SERVER, true));
+ return ['status' => 'error', 'error' => 'Shibboleth metadata missing!'];
+ }
+ // Query database for user
+ // First, use persistent-id as-is
+ $shibId = [ md5($_SERVER['persistent-id']) ];
+ // ... but we might have multiple persistent IDs, split
+ if (strpos($_SERVER['persistent-id'], ';') !== false) {
+ foreach (explode(';', $_SERVER['persistent-id']) as $s) {
+ if (empty($s))
+ continue;
+ $shibId[] = md5($s);
+ }
+ }
+ // Figure out role
+ if (strpos(";{$_SERVER['entitlement']};", CONFIG_ENTITLEMENT) !== false) {
+ $role = 'TUTOR';
+ } else if (strpos(";{$_SERVER[CONFIG_SCOPED_AFFILIATION]};", ';employee@') !== false
+ || strpos(";{$_SERVER[CONFIG_SCOPED_AFFILIATION]};", ';staff@') !== false
+ || strpos(";{$_SERVER[CONFIG_SCOPED_AFFILIATION]};", ';faculty@') !== false) {
+ $role = 'TUTOR';
+ } else {
+ file_put_contents('/tmp/shib-student-' . time() . '-' . $_SERVER['REMOTE_ADDR'] . '.txt', print_r($_SERVER, true));
+ $role = 'STUDENT';
+ // NEW: Ignore students for now
+ return [
+ 'status' => 'error',
+ 'error' => "Sie wurden als Student eingestuft und können sich daher nicht an der " . CONFIG_SUITE . "-Suite anmelden."
+ . "\nFalls Ihr Nutzerkonto kein Studentenkonto ist stellen Sie sicher, dass Ihr IdP für berechtigte"
+ . "\nAccounts entweder das " . CONFIG_SUITE . "-Entitlement ausliefert, oder das Attribut " . CONFIG_SCOPED_AFFILIATION
+ . "\nausgeliefert wird, und es entweder 'employee@..', 'staff@..' oder 'faculty@..' enthält."
+ . "\n\nMehr Informationen finden Sie unter " . CONFIG_HELPURL
+ ];
+ // end IGNORE STUDENTS
+ }
+ // Now we have an array of all persistent-ids, plus the raw string; see if any of those match
+ $user = Database::queryFirst("SELECT user.userid, user.organizationid, user.firstname, user.lastname, user.email "
+ . " FROM user "
+ . " INNER JOIN organization USING (organizationid) "
+ . " WHERE user.shibid IN (:shibid) LIMIT 1", array('shibid' => $shibId));
+ if ($user === false) {
+ // Not found, so we don't know which satellite to use
+ if ($role === 'STUDENT') {
+ $response['status'] = 'ok';
+ $response['firstName'] = $_SERVER['givenName'] ?? 'Anonymous';
+ $response['lastName'] = $_SERVER[CONFIG_SURNAME] ?? 'Student';
+ $response['mail'] = $_SERVER['mail'] ?? 'void@none.invalid';
+ $response['userId'] = $shibId;
+ // Try to figure out orgId
+ if (!isset($response['organizationId']) && isset($_SERVER[CONFIG_EPPN])) {
+ if (preg_match('/@(.+)$/', $_SERVER[CONFIG_EPPN], $out)) {
+ $out = Database::queryFirst("SELECT organizationid FROM organization_suffix WHERE suffix = :suffix", [
+ 'suffix' => $out[1]
+ ]);
+ if ($out !== false) {
+ $response['organizationId'] = $out['organizationid'];
+ }
+ }
+ }
+ if (!isset($response['organizationId']) && isset($_SERVER[CONFIG_SCOPED_AFFILIATION])) {
+ if (preg_match('/(^|;)[^@]+@([^;]+)/', $_SERVER[CONFIG_SCOPED_AFFILIATION], $out)) {
+ $out = Database::queryFirst("SELECT organizationid FROM organization_suffix WHERE suffix = :suffix", [
+ 'suffix' => $out[2]
+ ]);
+ if ($out !== false) {
+ $response['organizationId'] = $out['organizationid'];
+ }
+ }
+ }
+ // This one we send to the running master server handler
+ $rpc = $response;
+ $rpc['role'] = $role;
+ if (isset($response['organizationId']) && $accessCode === null) {
+ $response['satellites2'] = self::getSatelliteList($response['organizationId']);
+ }
+ } else {
+ $response['status'] = 'unregistered';
+ $response['error'] = 'Sie müssen sich erst für die Nutzung von ' . CONFIG_SUITE . ' registrieren';
+ }
+ $response['id'] = $shibId;
+ $response['url'] = CONFIG_MASTERWEBIF;
+ file_put_contents('/tmp/shib-unreg-' . time() . '-' . $_SERVER['REMOTE_ADDR'] . '.txt', print_r($_SERVER, true));
+ } else {
+ /*
+ if (in_array($shibId, unserialize(CONFIG_ADMINS), true) || $shibId === '2fa5c3e020a5aca0cbf9a562268d5173-') {
+ $role = 'STUDENT';
+ }
+ */
+ // Found, see if we got personal information, either temporarily through metadata, or from database
+ $firstName = $user['firstname'];
+ $lastName = $user['lastname'];
+ $mail = $user['email'];
+ if (empty($firstName) && isset($_SERVER['givenName']))
+ $firstName = trim($_SERVER['givenName']);
+ if (empty($lastName) && isset($_SERVER[CONFIG_SURNAME]))
+ $lastName = trim($_SERVER[CONFIG_SURNAME]);
+ if (empty($mail) && isset($_SERVER['mail']))
+ $mail = trim($_SERVER['mail']);
+ //
+ $login = (empty($user['userid']) ? $shibId : $user['userid'] );
+ if (empty($firstName) || empty($lastName) || empty($login)) {
+ // This means the user did not provide personal information on signup, nor does the IdP send them
+ $response['status'] = 'anonymous';
+ $response['error'] = "Ihr IdP liefert nicht die erforderlichen Attribute\n"
+ . CONFIG_SURNAME . ', ' . 'givenName' . ', ' . 'email';
+ } else {
+ // Seems ok!
+ //
+ $response['status'] = 'ok';
+ $response['firstName'] = $firstName;
+ $response['lastName'] = $lastName;
+ $response['mail'] = $mail;
+ $response['userId'] = $user['userid'];
+ $response['organizationId'] = $user['organizationid'];
+ // This one we send to the running master server handler
+ $rpc = $response;
+ $rpc['userId'] = $login;
+ $rpc['role'] = $role;
+ // This one we only send to the user
+ $response['satellites2'] = self::getSatelliteList($user['organizationid']);
+ }
+ }
+
+ if (isset($rpc)) {
+ if ($accessCode !== null) {
+ $rpc['accessCode'] = $accessCode;
+ }
+ $reply = RPC::submit($rpc);
+ if (preg_match('/^TOKEN:(\w+) SESSIONID:(\w+)$/', $reply, $out)) {
+ // For talking to the sat server, also referred to as userToken in Java
+ $response['token'] = $out[1];
+ // For talking to the master server - not known by satellite server, referred to as masterToken in Java
+ $response['sessionId'] = $out[2];
+ } else {
+ if (empty($rpc['mail'])) {
+ $reply .= ' (No email given)';
+ }
+ if (empty($rpc['firstName'])) {
+ $reply .= ' (No first name given)';
+ }
+ if (empty($rpc['lastName'])) {
+ $reply .= ' (No last name given)';
+ }
+ if (empty($rpc['organizationId'])) {
+ $reply .= ' (No organization id found)';
+ }
+ $response['error'] = $reply;
+ $response['status'] = 'error';
+ }
+ }
+ return $response;
+ }
+
+ public static function login(?string $accessCode = null): array
+ {
+ $res = self::loginInternal($accessCode);
+ if ($res['status'] !== 'ok' && isset($res['error']) && $accessCode !== null) {
+ RPC::submit(['status' => 'error', 'error' => $res['error'], 'accessCode' => $accessCode]);
+ }
+ return $res;
+ }
+
+ private static function getSatelliteList($orgId)
+ {
+ // Determine satellite(s)
+ $res = Database::simpleQuery("SELECT satellitename, addresses, certsha256 FROM satellite"
+ . " WHERE organizationid = :organizationid AND userid IS NULL", array('organizationid' => $orgId));
+ $sat2 = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $addrs = json_decode($row['addresses'], true);
+ if (!is_array($addrs) || empty($addrs))
+ continue;
+ $sat2[$row['satellitename']] = array(
+ 'addresses' => $addrs,
+ 'certHash' => $row['certsha256']
+ );
+ }
+ return $sat2;
+ }
+
+} \ No newline at end of file