<?php
// #######################################################   
// Author: Martin Haase / DAASI International GmbH / TextGrid    
// Creation date: 2010-10-13     
// Modification date: 2010-10-13
// Version: 0.1  
// based on TextGrid-Webauth.php     
// #######################################################   

mb_internal_encoding("UTF-8");

class RBAC {

  // Global variables  
  protected $authZinstance;

  protected $rbacbase;
  protected $sessionCreatorUid;
  protected $sessionCreatorPw;
  protected $setnamessecret;
  
  protected $soapExtra;
  protected $soapPolicy;
  protected $soapReview;
  protected $soapAdministration;

  protected $userAttributes;

  protected $SLCdata;

  public function __construct( $configfilepath , $authZinstance ) {
    require_once( "soapTypes.inc.php" );

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

    $this->rbacbase = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:rbacbase")->item(0)->nodeValue;
    if (!isset ($this->rbacbase) || strlen($this->rbacbase) == 0) {
	echo "Error: '${authZinstance}' has no RBAC base configured!<br/>\n";
	return null;
    }
    $this->authZinstance = $authZinstance;
    $this->sessionCreatorUid = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:sessioncreator/c:user")->item(0)->nodeValue;
    $this->sessionCreatorPw = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:sessioncreator/c:password")->item(0)->nodeValue;
    $this->setnamessecret = $xpath->query("/c:conf/c:authz[@instance='${authZinstance}']/c:setnamessecret")->item(0)->nodeValue;

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

    // -----------------------------------------------------
    // You'll need these services
    // -----------------------------------------------------

    $this->soapExtra = new SoapClient( $this->rbacbase . "wsdl/tgextra.wsdl" );
    $this->soapPolicy = new SoapClient( $this->rbacbase . "wsdl/tgsystem.wsdl" );
    $this->soapReview = new SoapClient( $this->rbacbase . "wsdl/tgreview.wsdl" );
    $this->soapAdministration = new SoapClient( $this->rbacbase . "wsdl/tgadministration.wsdl" );

  }

  public function slcData() {
    return $this->SLCdata;
  }

  public function createSession ( $remote_user ) {

    $rbachash = array("scstatus" => "", // will collect all messages during authentication    
		      "Sid" => "none", // this will hold the final sessionId then
		      "rbacbase" => $this->rbacbase,
		      'authZinstance' => $this->authZinstance
		      );

    // ------------------------------------------------------------
    // check whether remote_user is set and has user@domain format
    if (preg_match('/([^@]+)@([^@]+)/', $remote_user, $matches) == 1) { 
	$identified_user = array("authnmethod" => "eduPersonPrincipalName",
				 "user" => $matches[1],
				 "scope" => $matches[2] );

	$identified_user['user'] = $this->escapeForDN ($identified_user['user']);
	$identified_user['scope'] = $this->escapeForDN ($identified_user['scope']);

	$remote_user = $identified_user['user'] . "@" . $identified_user['scope'];

	$rbachash["identified_user"] = $identified_user;
	$rbachash["remote_user"] =  $remote_user; // TG User ID

    } else {
      return array("success" => FALSE,
		   "detail" => "User ID not given by home institution.<br/>\n"
		   ."Your school (its Identity Provider) did not provide a useable User ID to TextGrid (".$remote_user .").<br/>\n"
		   ."Please contact your school's computing centre and ask them to release to TextGrid's Service Provider "
		   ."the eduPersonPrincipalName attribute.<br/>Thank you.",
		   "rbachash" => $rbachash);
      exit;
    }
    $rbachash["scstatus"] .= "Authentication Method: ". $identified_user['authnmethod'] . "; ";


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

    try {
      $serviceAuthResponse = $this->soapExtra->authenticate( $serviceAuthReq );

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

    } catch( SoapFault $f ) {
      return array("success" => FALSE,
		   "detail" => "SOAP FAULT (authenticate)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail,
		   "rbachash" => $rbachash);
      exit;
    }


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

    try {
      $addRoleResponse = $this->soapExtra->tgAddActiveRole( $addRoleReq );

      if( $addRoleResponse->result ) {
	$rbachash["scstatus"] .= "Added active role of application; ";
      } else {
	return array("success" => FALSE,
		     "rbachash" => $rbachash,
		     "detail" => "Internal Error, Could not add Role for application.");
	exit;
      } 
    } catch( SoapFault $f ) {
      return array("success" => FALSE,
		   "rbachash" => $rbachash,
		   "detail" => "SOAP FAULT (tgAddActiveRole)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail);
      exit;
    }

    // -----------------------------------------------------
    // Check whether user exists already in RBAC
    // -----------------------------------------------------
    $userexistreq = new userExistsRequest();
    $userexistreq->auth = $serviceAuthResponse->auth;
    $userexistreq->username = $remote_user;

    try {
      $existresult = $this->soapExtra->userExists($userexistreq);
      if (! $existresult->result) {
	
	// -----------------------------------------------
	// User does not exist, so add 'em
	$adduserrequest = new addUserRequest();
	$adduserrequest->intSid = $serviceAuthResponse->auth;
	$adduserrequest->username = $remote_user;
	$adduserrequest->password = "gnuelpfix"; // this is not relevant and will never be checked
	
	try {
	  $addedUser = $this->soapAdministration->addUser($adduserrequest);
	  if ($addedUser) {
	    $rbachash["scstatus"] .= "Added user information to authorization database; ";
	  } else {
	    return array("success" => FALSE,
			 "rbachash" => $rbachash,
			 "detail" => "Could not add your user ID to authorization database." );
	    exit;
	  }
      
	} catch(SoapFault $f) {
	  return array("success" => FALSE,
		       "rbachash" => $rbachash,
		       "detail" => "SOAP FAULT (AddUser)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail );
	  exit;
	}
      } else {
	$rbachash["scstatus"] .=  "user exists in authentication database; ";
      }
    } catch (SoapFault $f) {
      return array("success" => FALSE,
		   "rbachash" => $rbachash,
		   "detail" => "SOAP FAULT (UserExists)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail );
      exit;
    }

    // -----------------------------------------------------
    // If this was successful you can create the session for remote_user
    // need to add active roles to the session 
    // -----------------------------------------------------
    $creReq = new createSessionRequest();
    $creReq->intSid = $serviceAuthResponse->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 = $serviceAuthResponse->auth;
    $rolesobject->username = $remote_user;

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

      $rbachash["scstatus"] .=  "Received all available roles for user; ";
    } catch (Soapfault $f) {
      return array("success" => FALSE,
		   "rbachash" => $rbachash,
		   "detail" => "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 a newly generated sid from the RBAC system
    try {
      $Sid = $this->soapExtra->getSid();
      $Sid = $Sid->sid;
      $rbachash["Sid"] = $Sid;
    } catch (Soapfault $f) {
      return array("success" => FALSE,
		   "rbachash" => $rbachash,
		   "detail" => "RBAC down? Could not generate a new SessionID!" );
      exit;
    }

    // -------------------------------------
    // Creating the session...
    $creReq->sid = $Sid;
    try {
      $creResponse = $this->soapPolicy->createSession( $creReq );

      if( $creResponse->result ) {

	$rbachash["scstatus"] .= "Created sessions with active roles; ";
      } else {
	return array("success" => FALSE,
		     "rbachash" => $rbachash,
		     "detail" => "Failed to create a new Session!" );
	exit;
      }

    } catch (SoapFault $f) {
      return array("success" => FALSE,
		   "rbachash" => $rbachash,
		   "detail" => "SOAP FAULT (CreateSession)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail );
      exit;
    }

    // --------------------------------------------------------
    // now all went well, createSession worked, return success
    return array("success" => TRUE,
		 "rbachash" => $rbachash);
  }

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

  function getUserAttributes ( $Sid ) {
    $getMyUAR = new StdClass();
    $getMyUAR->auth = $Sid;

    try {
      $ua = $this->soapExtra->getMyUserAttributes($getMyUAR);
      $this->userAttributes = $ua->attribute;
      return $ua->attribute;
    } catch (SoapFault $f) {
      return array("success" => FALSE,
		   "detail" => "SOAP FAULT (getMyUserAttributes)!: " . serialize ($f) );
    }

  }

  function enoughUserAttributes ( $Sid ) {
    if (!isset($this->userAttributes)) {
      $this->getUserAttributes( $Sid );  
    }
    //       $file = fopen ("/tmp/xxxR.log", "w+");
    //   fwrite ($file, serialize ($this->userAttributes ) ."\n");
    //   fclose ($file);

    foreach ($this->userAttributes as $a) {
      if ($a->mandatory) {
	if (!isset($a->value)) {
	  return FALSE;
	} else {
	  if (is_string($a->value) && strlen($a->value) < 1) {
	    return FALSE;
	  }
	}
      }
    }

    return TRUE;
  }

  function updateAttributes ( $attrs, $map, $Sid ) {
    $newattributes = array();
    foreach ($map as $name => $value) {
      if (isset($attrs[$value])) {
	$na = new StdClass();
	$na->name = $name;
	$na->value = $attrs[$value];
	$newattributes[] = $na;
      }
    }
    return $this->setAttributes ($newattributes, $Sid, TRUE);
  }

  function setAttributes ( $attrs, $Sid, $loginmode ) {
    $setMyUserAttributesRequest = new StdClass();
    $setMyUserAttributesRequest->attribute = $attrs; 
    $setMyUserAttributesRequest->auth = $Sid;
    if ($loginmode) {
      $setMyUserAttributesRequest->webAuthSecret = $this->setnamessecret;
    } else {
      $setMyUserAttributesRequest->webAuthSecret = "";
    }

    try {
      $res = $this->soapExtra->setMyUserAttributes($setMyUserAttributesRequest);
      return $res;
    } catch (SoapFault $f) {
      return array("success" => FALSE,
		   "detail" => "SOAP FAULT (setMyUserAttributes)!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail );
    }
  }

}

?>