<?php
// #######################################################
// Authors: Markus Widmer & Martin Haase
// Creation date: 08.07.2007
// Modification date: 07/05/010
// Version: 2.0
// #######################################################

header('Content-Type: text/html; charset=utf-8'); 

//phpinfo();
require_once( "soapTypes.inc.php" );

$config = new DOMDocument();
$config->load('../../../config_tgwebauth.xml');
$xpath = new DOMXPath($config);
$xpath->registerNamespace("c", "http://textgrid.info/namespaces/middleware/tgwebauth");


$authZinstance = $_REQUEST["authZinstance"];
$rbacInstance = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']");
if ($rbacInstance->length == 0) 
  {
   echo "Error: '${authZinstance}' has no RBAC base configured!<br/>\n";
   exit;
 }

$rbacbase = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:rbacbase")->item(0)->nodeValue;
$sessionCreatorUid = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:sessioncreator/c:user")->item(0)->nodeValue;
$sessionCreatorPw = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:sessioncreator/c:password")->item(0)->nodeValue;
$setnamessecret = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:setnamessecret")->item(0)->nodeValue;

$slcSupportEnabling = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:SLCsupport/@enable");
if ($slcSupportEnabling->length > 0 && $slcSupportEnabling->item(0)->nodeValue === 'true') {
   $slcMode = TRUE;
   $slcEntitlementAttributeName = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:SLCsupport/c:entitlementAttr/@name")->item(0)->nodeValue;
   $slcEntitlementAttributeValue = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:SLCsupport/c:entitlementAttr")->item(0)->nodeValue;
   $slcPortalDelegationURL = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:SLCsupport/c:portalDelegationURL")->item(0)->nodeValue;
   $slcNoDelegationURL = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:SLCsupport/c:noDelegationURL")->item(0)->nodeValue;
} else {
   $slcMode = FALSE;
}

// the session creation status will collect all messages
// during the course of authentication
$scstatus = "";

$remote_user = $_SERVER["REMOTE_USER"];
$identity_provider = $_SERVER["Shib-Identity-Provider"];
if (isset($_SERVER["givenName"])) { $givennames =    $_SERVER["givenName"];} else { $givennames = "";}
if (isset($_SERVER["sn"])) {        $surnames =      $_SERVER["sn"];}        else { $surnames = "";}
if (isset($_SERVER["cn"])) {        $cns =           $_SERVER["cn"];}        else { $cns = "";}
if (isset($_SERVER["mail"])) {      $mails =         $_SERVER["mail"];}      else { $mails = "";}
if (isset($_SERVER["o"])) {         $organisations = $_SERVER["o"];}         else { $organisations = "";}


$identified_user = identify($remote_user, $identity_provider);

if ($identified_user['authnmethod'] == 'none') {
   format_error("User ID not given by home institution",
   		"Your school (its Identity Provider) did not provide a useable User ID to TextGrid.<br/>\n"
		."Please contact your school's computing centre and ask them to release to TextGrid's Service Provider "
		."at least one of the following attributes: eduPersonPrincipalName, PersistentID or TargetedID. Thank you.");
   exit;
} else {
   $scstatus .= "Authentication Method: ". $identified_user['authnmethod'] . "; ";
   $remote_user = $identified_user['user'] . "@" . $identified_user['scope'];
}


// -----------------------------------------------------
// You'll need these services
// -----------------------------------------------------
$soapExtra = new SoapClient( $rbacbase . "wsdl/tgextra.wsdl" );
$soapPolicy = new SoapClient( $rbacbase . "wsdl/tgsystem.wsdl" );
$soapReview = new SoapClient( $rbacbase . "wsdl/tgreview.wsdl" );
$soapAdministration = new SoapClient( $rbacbase . "wsdl/tgadministration.wsdl" );


// -----------------------------------------------------
// Before you can create a session you have to
// authenticate. If this was successful you get a
// session-ID that you should keep
// -----------------------------------------------------
$authReq = new authenticateRequest();
$authReq->username = $sessionCreatorUid;
$authReq->password = $sessionCreatorPw;


//echo "<HR/>";
//echo "Doing authentication...<BR/>";

try {
  $authResponse = $soapExtra->authenticate( $authReq );

  if( preg_match( "/[0-9a-z]{2,}/i", $authResponse->auth ) ) {
    $scstatus .= "WebAuth authenticated at RBAC, received an internal SessionId. " ;
  }

} catch( SoapFault $f ) {
   format_error("Internal Error",
   		"SOAP FAULT (authenticate)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail );
   exit;
}



// -----------------------------------------------------
// Now you can try to add an active role to your session creator session
// -----------------------------------------------------
$addRoleReq = new addActiveRoleRequest();
$addRoleReq->username = $sessionCreatorUid;
$addRoleReq->role = "sessionCreator,Anwendung";
$addRoleReq->auth = $authResponse->auth;

//echo "<HR/>";
//echo "Adding active role...<BR/>";

try {

  $addRoleResponse = $soapExtra->tgAddActiveRole( $addRoleReq );

  if( $addRoleResponse->result ) {
    
    //echo "DONE.<BR/>";
     $scstatus .= "Added active role of application; ";

  } else {
   format_error("Internal Error",
                "Could not add Role for application.");
   exit;
  } 
}
catch( SoapFault $f ) {
   format_error("Internal Error",
                "SOAP FAULT (tgAddActiveRole)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail );
   exit;
}


$userexistreq = new userExistsRequest();
$userexistreq->auth = $authResponse->auth;
$userexistreq->username = $remote_user;

try {
  $existresult = $soapExtra->userExists($userexistreq);
  if (! $existresult->result) {
    try {
      $adduserrequest = new addUserRequest();
      $adduserrequest->intSid = $authResponse->auth;
      $adduserrequest->username = $remote_user;
      $adduserrequest->password = "gnuelpfix"; // this is not relevant and will never be checked
      
      $addedUser = $soapAdministration->addUser($adduserrequest);
      if ($addedUser) {
	      $scstatus .= "Added user information to authorization database; ";
      } else {
      	 format_error("Internal Error",
                      "Could not add your user ID to authorization database." );
   	 exit;
      }	
      
    } catch(SoapFault $f) {
      format_error("Internal Error",
                   "SOAP FAULT (AddUser)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail );
      exit;
    }
  } else {
    $scstatus .=  "user exists in authentication database; ";
  }
} catch (SoapFault $f) {
   format_error("Internal Error",
                "SOAP FAULT (UserExists)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail );
   exit;
}


// -----------------------------------------------------
// If this was successful you have to add an appropriate
// role to your active session that allows you to create
// a session for someone else.
// -----------------------------------------------------
$creReq = new createSessionRequest();
$creReq->intSid = $authResponse->auth;
//$creReq->username = "mhaase@uni-tuebingen.de";
$creReq->username = $remote_user;

//$creReq->roleset = Array( "Projektleiter,Projekt-1,Projekt-Teilnehmer" );
//$creReq->roleset = Array( "Bearbeiter,Projekt-1,Projekt-Teilnehmer" );

// get ALL available roles...
$rolesobject = new authorizedRolesRequest();
$rolesobject->intSid = $authResponse->auth;
$rolesobject->username = $remote_user;

try {
  $roleResponse = $soapReview->authorizedRoles($rolesobject);

// cannot list roles here as they contain "s, which interfere with the 
// attribute eclosing quotes of the meta tag
//  $scstatus = $scstatus . "Received all available roles for user: ".serialize($roleResponse->role) . "; ";
  $scstatus .=  "Received all available roles for user; ";
} catch (Soapfault $f) {
   format_error("Internal Error",
                "SOAP FAULT (authorizedRoles)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail );
   exit;
}


if (is_Array($roleResponse->role)) {
  $creReq->roleset = $roleResponse->role;
} elseif (is_string($roleResponse->role)) {
  $creReq->roleset = Array($roleResponse->role);
} else {
  $creReq->roleset = Array();
}


// Get an newly generated sid from the RBAC system
try {
  $newSid = $soapExtra->getSid();
  $newSid = $newSid->sid;
} catch (Soapfault $f) {
   format_error("Internal Error",
                "RBAC down? Could not generate a new SessionID!" );
   exit;
}


$creReq->sid = $newSid;
//echo "Creating the session...<BR/>";
try {
  $creResponse = $soapPolicy->createSession( $creReq );

  if( $creResponse->result ) {
    //echo "DONE.<BR/>";
    $scstatus .= "Created active role; ";
  } else {
    $scstatus .= "Could not create active role, proceeding without any role(s) in the session; ";
  }

} catch (SoapFault $f) {
   format_error("Internal Error",
                "SOAP FAULT (CreateSession)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail );
   exit;
}

// We will arrive here only if all went well. 
// Otherwise, format_error() will be called which prints its own header and footer

// Header ------------------------------------------------------------
echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
echo "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">\n";
echo "<head>\n";
echo "<title>Authentication Succeeded</title>\n";
echo "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\" />\n";

// Data --------------------------------------------------------------
echo "<meta name=\"remote_principal\" content=\"$remote_user\"/>\n";
echo "<meta name=\"rbac_session_status\" content=\"$scstatus\"/>\n";
echo "<meta name=\"rbac_sessionid\" content=\"$newSid\"/>\n";
if (!$slcMode) { 
   echo "<meta name=\"ePPNplusSID\" content=\"$remote_user|$newSid\"/>\n";
}
echo "<style type=\"text/css\">";
echo "#d123 {display:none;}";
echo "</style>";
echo "<script type=\"text/javascript\">\n";
echo "<!--\n";
echo "function toggle (target) {\n";
echo "	var obj=document.getElementById(target);\n";
echo "	obj.style.display=\"block\";\n";
echo "}\n";
echo "-->\n";
echo "</script>\n";

echo "</head>\n<body>\n";
  echo "<h2>Authentication Succeeded</h2>\n"; 

  echo "<p>You were successfully authenticated with User ID '<b>" . $remote_user . "</b>'. You may now access remote resources using the TextGrid Lab. This window can be closed.</p>\n";

  // experimental: Logout
  //echo "<br/><br/>";

  // das hier beendet nur die SP-Session, IdP bleibt...
  //echo "Click <a href=\"https://auth.textgrid.daasi.de/Shibboleth.sso/Logout\">here</a> if you want to log out.";

  // das hier sollte den Browser schließen, tuts aber nicht...
  //echo "<form action=\"\"><input type=\"button\" value=\"Log Out\" onclick=\"window.close()\"></form>";

  // also cookies loeschen:
  //s. http://de.selfhtml.org/javascript/objekte/document.htm#cookie...

// Details -----------------------------------------------
echo "<p>More <a href=\"javascript:toggle('d123')\">Details</a>.</p>\n";
echo "<div id=\"d123\"> <h2>Authentication Details</h2>\n";
echo "<table><tr><td>TgAuth Instance</td><td>". $rbacbase ."</td></tr>\n";
echo "<tr><td>Shibboleth Identity ProviderID</td><td>". $identity_provider  ."</td></tr>\n";
echo "<tr><td>User ID Attribute Name</td><td>". $identified_user['authnmethod'] ."</td></tr>\n";
echo "<tr><td>User ID Value </td><td>".$remote_user."</td></tr>\n";
echo "<tr><td>Given Name(s)</td><td>".$givennames."</td></tr>\n";
echo "<tr><td>Surname(s)</td><td>".$surnames."</td></tr>\n";
echo "<tr><td>Common Name(s)</td><td>".$cns."</td></tr>\n";
echo "<tr><td>Mail(s)</td><td>".$mails."</td></tr>\n";
echo "<tr><td>Organisation(s)</td><td>".$organisations."</td></tr>\n";
echo "<tr><td>TgAuth Session ID</td><td>".$newSid."</td></tr></table>\n";
echo "</div>";	

setNameInRBAC();

if ($slcMode) {
   $isSLCScompatible = scanEntitlements();
   if ($isSLCScompatible) {
      showCertificateButtons();
   } else {
      showCertificateInfoButton();
   }
}


echo "\n</body>\n</html>";
  
exit;

/////////////////////// Functions ///////////////////////////////////////
function format_error ($heading, $detail) {

global $remote_user, $scstatus, $newSid, $rbacbase, $identity_provider, $identified_user;

// Header ------------------------------------------------------------
echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
echo "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">";
echo "<head>\n";
echo "<title>Authentication Failed</title>\n";
echo "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\" />\n";

echo "<meta name=\"remote_principal\" content=\"$remote_user\"/>\n";
echo "<meta name=\"rbac_session_status\" content=\"$scstatus\"/>\n";
echo "<meta name=\"rbac_sessionid\" content=\"$newSid\"/>\n";
echo "<style type=\"text/css\">";
echo "#d123 {display:none;}";
echo "</style>";
echo "<script type=\"text/javascript\">\n";
echo "<!--\n";
echo "function toggle (target) {\n";
echo "  var obj=document.getElementById(target);\n";
echo "  obj.style.display=\"block\";\n";
echo "}\n";
echo "-->\n";
echo "</script>\n";
echo "</head>\n\n<body>\n";

echo "<h2>Authentication Failure, $heading</h2>\n";
echo "<p>The Authentication system could not authenticate you.</p>\n";

echo "<p>More <a href=\"javascript:toggle('d123')\">Details</a>.</p>\n";
echo "<div id=\"d123\">"; 
echo "<h2>Error Details</h2>\n";
echo "<p>". $detail ."</p>" ;
echo "<h2>Authentication Details</h2>\n";
echo "<table><tr><td>TgAuth Instance</td><td>". $rbacbase ."</td></tr>\n";
echo "<tr><td>Shibboleth Identity ProviderID</td><td>". $identity_provider  ."</td></tr>\n";
echo "<tr><td>User ID Attribute Name</td><td>". $identified_user['authnmethod'] ."</td></tr>\n";
echo "<tr><td>User ID Value </td><td>".$remote_user."</td></tr>\n";
echo "<tr><td>TgAuth Session ID</td><td>".$newSid."</td></tr></table>\n";
echo "</div>";

echo "<p>If not indicated otherwise in the <a href=\"javascript:toggle('d123')\">details</a>, it could be that some service is not responding temporarily. In this case, please <a href=\"javascript:history.back()\">go back</a> or re-open the TextGridLab and try again.</p>";
echo "<p>If the problem persists, please report this bug together with its time of occurence (" . date("Y-m-d H:i:s") .  "). In the TextGridLab, choose 'Help-&gt;Report Bug'.</p>" ;

echo "\n</body>\n</html>";
}


function identify ( $remote_user, $idp ) 
{
  $authnmethod = "undefined";
  $user = "dummy";
  $scope = "no-scope.xxx";
  if (preg_match('/([^@]+)@([^@]+)/', $remote_user, $matches) == 1) 
    { 
      $authnmethod = "ePPN";
      $user = $matches[1];
      $scope = $matches[2];
    }
  else if (preg_match('/([^!]+)!([^!]+)!([^!]+)/', $remote_user, $matches) == 1)
    { 
      $authnmethod = "persistentId";
      $user = $matches[3];
      $scope = $idp;
    }
  else if (strlen($remote_user) > 0) 
    {
      $authnmethod = "targetedId";
      $user = $remote_user;
      $scope = $idp;
    }
  else 
    {
      $authnmethod = "none";
    }

  $user = escapeForDN($user);
  $scope = escapeForDN($scope);

  return array("authnmethod" => $authnmethod,
  	       "user" => $user,
	       "scope" => $scope );
}


function escapeForDN ($string) 
{
  return preg_replace('/[";+<>,\\\]/', "X", $string);
}

function scanEntitlements () {
    global $slcEntitlementAttributeName, $slcEntitlementAttributeValue;

    if (isset($_SERVER[$slcEntitlementAttributeName])) {
        $entitlements = $_SERVER[$slcEntitlementAttributeName];
        $arrEntitlements = explode( ";", $entitlements);
        foreach ($arrEntitlements as $ent) {
            if ($ent === $slcEntitlementAttributeValue) {
		 return TRUE;
            }
	}
    }
    return FALSE;
}


function showCertificateInfoButton () {
    global $slcNoDelegationURL, $remote_user, $newSid;

    echo "<br/><br/>Your account does not include certificate support.";
    echo "<form method=\"get\" action=\"${slcNoDelegationURL}\">\n";
    echo "<input type=\"hidden\" name=\"ePPNplusSID\" value=\"" . $remote_user . "|" .$newSid . "\" />\n";
    echo "<input type=\"submit\" value=\"Work without Certificate\"/>\n";
    echo "</form>\n";
}

function showCertificateButtons () {
    global $slcPortalDelegationURL, $slcNoDelegationURL, $remote_user, $newSid, $authZinstance;
  
    echo "<form method=\"get\" action=\"${slcPortalDelegationURL}\">\n";
    echo "<input type=\"hidden\" name=\"userDetails\" value=\"" . $remote_user . "|" .$newSid ."|". $authZinstance . "\">\n";
    echo "<input type=\"submit\" value=\"Request Certificate\">\n";
    echo "</form>\n\n";

    echo "<form method=\"post\" action=\"${slcNoDelegationURL}\">\n";
    echo "<input type=\"hidden\" name=\"ePPNplusSID\" value=\"" . $remote_user . "|" .$newSid . "\" />\n";
    echo "<input type=\"submit\" value=\"Use Existing Certificate\"/>\n";
    echo "</form>\n";
}



function setNameInRBAC ()
{

  global $givennames, $surnames, $cns, $mails, $organisations, $soapExtra, $newSid, $identity_provider, $remote_user, $setnamessecret;
  $setNameReq = new setNameRequest();
  $setNameReq->auth = $newSid;
  $setNameReq->log = "";
  $setNameReq->webAuthSecret = $setnamessecret;

  // name will be first cn with a space (s.t. no uid), or "gn1 gn2 gn3 sn1 sn2", or last resort ePPN
  $cnarr = preg_split ("/;/", $cns );
  if ( sizeof ($cnarr) > 0  && preg_match("/ /", $cns) > 0 ) 
    {
      for ($i = 0; $i < sizeof ($cnarr); $i++) 
	{
	  if (preg_match("/ /", $cnarr[$i]) > 0 ) 
	    {
	      $setNameReq->name = $cnarr[$i];
	      break;
	    }
	}
    }
  elseif ( strlen ($givennames) > 0 && strlen ($surnames) > 0)
    {
      $givennameswithspaces = preg_replace ( "/;/", " ", $givennames );
      $surnameswithspaces = preg_replace ( "/;/", " ", $surnames );
      $setNameReq->name = $givennameswithspaces . " " . $surnameswithspaces;
    }
  else 
    {
      $setNameReq->name = $remote_user;
    }
  
  $setNameReq->mail = $mails;

  if ( strlen ($organisations) > 0) 
    {
      $setNameReq->organisation = $organisations;
    } 
  else 
    {
      $setNameReq->organisation = $identity_provider;
    }
  
  $setNameReq->agreeSearch = TRUE;

  try 
    {
      $setNameResult = $soapExtra->setName( $setNameReq );

      if (! $setNameResult->result ) {
	// do NOT exit as setName is not vital
 	echo "setName: result=false";
      }
    } 
  catch (Soapfault $f) 
    {
      // do NOT exit as setName is not vital
      echo "SoapFault";	
    }

}


?>