Configurare Google Apps in OpenAM come Service provider è molto semplice.
1. Dalla pagina principale, selezioniamo la tab Common Tasks, e clicchiamo su Configure Google Apps.
3. Clicchiamo su Create.
4. Ci verrrà presentata la seguente schermata:
5. Copiamo ed incolliamo i dati forniti nella pagina di configurazione di Google Apps (paragrafo 3.1).
6. Spuntiamo entrambe le caselle ( Attiva Single Sign On e Utilizza una società di emissione specifica).
7. Salviamo le modifiche.
8. Spostiamoci sulla pagina di accesso Gmail. Supponendo di aver creato un utente scinardi sia su OpenAM che su Gmail (l’uid deve essere identico), digitiamo nel campo Nome Utente [email protected] e accediamo; Si verrà rediretti alla pagina di login di OpenAM. Inserendo l’username scinardi e la corri- spettiva password, si accederà automaticamente alla pagina dell’utente in Google Apps.
4 Integrazione servizi interni mediante Policy Agent
Ogni azienda possiede dei servizi di back-end per gestire i propri prodotti. Questi servizi sono accessibili, nella maggior parte dei casi, da tutti i dipendenti ognuno dei quali, però, possiede permessi differenti. I Policy Agent di OpenAM permettono l’accesso dei dipendenti a determinati servizi valutando le loro autorizzazioni.
I policy Agent possono essere scaricati al seguente link: https://download.forgerock. com/#/openam.
Un policy agent applica una policy per OpenAM. I policy agent utilizzati in fase di progetto sono due:
• Web Policy Agent: installato sul server HTTP Apache, intercetta le richieste di un utente che prova ad accedere a risorse protette del web, e rigetta l’accesso fino a quando l’utente non ottiene le autorizzazione da OpenAM per accedervi. • J2EE policy agent: installato su un container di applicazioni web (Tomcat),
intercetta le richieste di un utente che prova ad accedere a risorse protette del web, e rigetta l’accesso fino a quando l’utente non ottiene le autorizzazione da OpenAM per accedervi.
I policy Agent vengono installati direttamente sul server (o container di applicazioni) web ospitante le risorse. Poichè questi policy agent presentano al loro interno la sola configurazione per la connessione con OpenAM, essi possono essere gestiti in maniera centralizzata direttamente dal pannello di amministrazione.
1. Dopo aver selezionato il nostro realm, creiamo un Policy Agent, cliccando su New.
3. É importante ricordare password e nome del policy, necessari in fase di installa- zione del policy agent direttamente sul server web che vogliamo controllare. 4. Impostiamo le policy di accesso. Spostiamoci sulla tab Policies e creiamo una
nuova policy.
5. La sezione Rules definisce la risorsa che verrà protetta, mentre la sezione Su- bjects gli utenti cui è consentito o meno l’accesso alla risorsa specificata in Rules.
6. L’ultimo passo è quello di installare il policy direttamente sul server web o con- tainer di applicazioni contenente le risorse.
7. Riavviamo Tomcat e, puntando il browser sulla risorsa protetta, verremo reindiriz- zati alla pagina di login di OpenAM: se l’utente, dopo essersi autenticato, possiede i permessi necessari, verrà reindirizzato alal risorsa, altrimenti verrà mostrata la pagina di errore 401 (Errore HTTP 401 Unauthorized).
5 Conclusione e Sviluppi futuri
Il lavoro svolto realizza una infrastruttura di gestione dell’accesso centralizzata, man- tenendo l’identità unica per ogni utente; è permesso, agli stessi, di accedere a tutte le risorse informatiche, utilizzando sempre le stesse credenziali di accesso. Viene, inol- tre, fornita la possibilità alle aziende di sfruttare la Strong Authentication offerta dal prodotto IambOOTP, il quale permette di utilizzare il proprio dispositivo mobile come generatore di OTP per una più sicura e facile autenticazione.
Il passo successivo dell’azienda Iamboo è quello di integrare in OpenAM un altro prodotto di OTP, Wito.
Wito è un sistema di autenticazione che sfrutta il numero di telefono (ID) del chiaman- te. Il funzionamento è molto semplice: un utente si registra ad un determinato servizio con nome utente, password e numero telefonico. A registrazione effettuata, esso ac- cede al servizio mediante username e password; immediatamente, il sistema effettua una chiamata al numero di telefono fornito in fase di registrazione. Non è necessario rispondere alla chiamata che proverrà da un numero sempre diverso nelle ultime cifre (es le ultime 4). L’utente dovrà semplicemente inserire le ultime quattro cifre e sarà immediatamente autenticato.
L’idea è quella di utilizzare questo sistema di Strong Authentication per l’autenticazione in OpenAM.
• L’utente che vuole accedere alle risorse protette, inserisce username e password. • OpenAM, verificata la correttezza delle credenziali, contatta il server Wito, il
quale fa partire la chiamata verso l’utente.
• L’utente, prelevate le 4 cifre finali, le inserisce nel pannello OpenAM, il quale effettuerà una nuova chiamata verso il server di validazione.
Parte VI
Ringraziamenti
Ringrazio il mio relatore, grazie al quale ho avuto la possibilità di sostenere la tesi presso l’azienda Iamboo e iniziare un’esperienza lavorativa presso di loro.
Ringrazio il gruppo di lavoro che mi ha accompagnato per tutto il periodo della tesi e mi accompagna tutt’ora: il mio tutor aziendale Cip, Giorgio, Baì, le “Paole”, Alessandro e Leonardo.
Un ringraziamento speciale va a Francesco (Cicciodoro) che mi accompagna dai tem- pi dell’amata Palermo e con il quale ho condiviso e, spero, condividerò esperienze indimenticabili . Sei un vero amico.
Ringrazio Enrico, con il quale, oltre ad una grande amicizia, ci lega il “disagio”: ce l’abbiamo fatta pure questa volta visto Sal?
Ringrazio la mia amica Assia, la quale “potrebbe far scatenare la terza guerra mondiale” se solo volesse. Per fortuna, non vuole.
Ringrazio Domenico, Sandro e tutto il gruppo del CUS Pisa Calcio: quest’anno è il nostro anno!
Ringrazio i miei ex coinquilini e quelli nuovi, per aver sopportato una persona distratta e disordinata come me.
Ringrazio tutti i miei amici, quelli che ci sono ancora e quelli che, mio malgrado, ci sono un pò meno.
E, ovviamente, un ringraziamento a parte va fatto alla mia famiglia.
A mia sorella, Valentina, con la quale ci “scorniamo” ogni volta che ci vediamo ma cui voglio un bene dell’anima.
A mio fratello Vittorio, nonostante ci si senta poco, sappi che sei sempre stato un esempio per me, anche se, a calcio sono io il più bravo!
Ed infine, a mamma e papà, Ada e Pippo, gli “artefici” di quello che sono adesso e di quello che, incrociando le dita, diventerò. Siete le persone più importanti della mia vita.
Parte VII
Listati
1 Modulo di autenticazione IambOOTP
1.1 SampleAuth.java
/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. *
* Copyright (c) 2011 ForgeRock AS. All Rights Reserved *
* The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License.
*
* You can obtain a copy of the License at * http://forgerock.org/license/CDDLv1.0.html
* See the License for the specific language governing * permission and limitations under the License. *
* When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at http://forgerock.org/license/CDDLv1.0.html
* If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]" * */ package com.forgerock.openam.iamboo; import it.iamboo.otp.library.OTPLibrary; import it.iamboo.otp.library.exceptions.BadServerResponseException; import it.iamboo.otp.library.exceptions.IambOOTPLibraryException; import it.iamboo.otp.library.exceptions.MustResyncException; import it.iamboo.otp.library.exceptions.OperationNotAllowedException; import java.io.IOException; import java.security.Principal; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback; import javax.security.auth.login.LoginException; import com.iplanet.dpro.session.service.InternalSession; import com.iplanet.sso.SSOException; import com.iplanet.sso.SSOToken; import com.iplanet.sso.SSOTokenManager; import com.sun.identity.authentication.spi.AMLoginModule; import com.sun.identity.authentication.spi.AuthLoginException; import com.sun.identity.authentication.spi.InvalidPasswordException; import com.sun.identity.authentication.util.ISAuthConstants; import com.sun.identity.shared.datastruct.CollectionHelper; import com.sun.identity.shared.debug.Debug;
public class SampleAuth extends AMLoginModule { // Name for the debug-log
// set MESSAGE as debug level in openam console
// Configuration->Servers and Sites->select Server Name ->Debug level->Message private final static String DEBUG_NAME = "IambOOTP";
// Name of the resource bundle
private final static String amAuthSampleAuth = "amAuthSampleAuth"; // IAMBOO Server parameters
private String ip=""; private int port= 0;
private String serial = "";
// Orders defined in the callbacks file private final static int STATE_BEGIN = 1; private final static int STATE_AUTH = 2; private final static int STATE_RESYNC = 3; private final static int STATE_ERROR = 4;
private final static Debug debug = Debug.getInstance(DEBUG_NAME); private Map options;
private ResourceBundle bundle; private String userName = null; private String userUUID = null; private Map sharedState;
//Path to retrieve the configuration file
private static final String sysEnvStr = System.getenv("CATALINA_HOME");
private static final String path = sysEnvStr+"/webapps/openam/config/config.cfg"; private List<String> valueeFromDb;
protected Principal userPrincipal; private OTPLibrary library; public SampleAuth() { super();
}
@Override
// This method stores service attributes and localized properties // for later use.
public void init(Subject subject, Map sharedState, Map options) { valueeFromDb = new ArrayList<String>();
if (debug.messageEnabled()) {
debug.message("iambOOTP::init:\n" + "\n****************************\n" + " Remember!! set the CATALINA_HOME environment variable as: \n" +
"/<path_tomcat_directory>. \nexample: /home/user/tomcat" + "\n****************************\n");
}
this.options = options;
bundle = amCache.getResBundle(amAuthSampleAuth, getLoginLocale()); try {
userName = (String) sharedState.get(getUserKey());
debug.message("\nIambOOTP:: Username: " + userName +"\n"); } catch (Exception e) {
debug.message("HOTP.init() : " + "Unable to set userName : ", e); }
this.sharedState = sharedState; }
@Override
public int process(Callback[] callbacks, int state) throws LoginException { try {
if (userName == null || userName.length() == 0) {
// session upgrade case. Need to find the user ID from the old // session
SSOTokenManager mgr = SSOTokenManager.getInstance();
InternalSession isess = getLoginState("HOTP").getOldSession(); if (isess == null) {
throw new AuthLoginException("amAuth", "noInternalSession",null); }
SSOToken token = mgr.createSSOToken(isess.getID().toString()); userUUID = token.getPrincipal().getName();
userName = token.getProperty("UserToken"); if (debug.messageEnabled()) {
debug.message("HOTP.process() : " + "UserName in SSOToken : " + userName); }
if (userName == null || userName.length() == 0) {
throw new AuthLoginException("amAuth", "noUserName", null); }
}
} catch (SSOException e) {
debug.error("HOTP.process() : " + "SSOException", e);
throw new InvalidPasswordException("amAuth", "invalidPasswd", null); }
if (debug.messageEnabled()) {
debug.message("IambOOTP:: (" +userName+ ") - process state: " + state); }
switch (state) { case STATE_BEGIN:
// No time wasted here - simply modify the UI and // proceed to next state
return STATE_AUTH; case STATE_AUTH:
// Get data from callbacks. Refer to callbacks XML file. NameCallback nc = (NameCallback) callbacks[0]; String enteredOTP = nc.getName();
/*
*/
JdbcConnect jdbccon = new JdbcConnect(path,userName); try {
valueeFromDb = jdbccon.readDataBase(); serial = valueeFromDb.get(1);
debug.message("IambOOTP:: (" +userName+ ") - serial: " + serial); /*
* If is not possible get serial from database, * no serial is associated with user
*/
} catch (Exception e1) { e1.printStackTrace(); }
try {
//Retrieve ip and port of server from configuration file ip = jdbccon.getServerIP();
port=jdbccon.getServerPort(); if (debug.messageEnabled())
debug.message("IambOOTP::IP Server:" +ip+":"+port); library = new OTPLibrary(ip,port);
library.authentication(serial,enteredOTP);
debug.message("IambOOTP:: (" +userName+ ") - Login succeed "); return ISAuthConstants.LOGIN_SUCCEED;
} catch (IOException e) { e.printStackTrace();
debug.message("IambOOTP::Exception IO" + e.getMessage() ); throw new InvalidPasswordException("exception", userName); }catch (MustResyncException e) {
debug.message("IambOOTP:: (" +userName+ ") - MustResyncException: " + e.getMessage() ); e.printStackTrace(); return STATE_RESYNC;
}catch (OperationNotAllowedException e) {
debug.message("IambOOTP:: (" +userName+ ") - OperationNotAllowedException: " + e.getMessage()); return STATE_RESYNC;
}catch (IambOOTPLibraryException e) {
debug.message("IambOOTP:: (" +userName+ ") - Exception IambOOTPLibraryException: " + e.getMessage()); throw new InvalidPasswordException("exception", userName);
} catch (BadServerResponseException e) {
debug.message("IambOOTP:: (" +userName+ ") - Exception BadServerResponseException: " + e.getMessage()); throw new InvalidPasswordException("exception", userName);
}
case STATE_RESYNC:
NameCallback first_nc_resync = (NameCallback) callbacks[0]; String first_enteredOTP_resync = first_nc_resync.getName(); NameCallback second_nc_resync = (NameCallback) callbacks[1]; String second_enteredOTP_resync = second_nc_resync.getName(); List<String> otps = new ArrayList<String>();
otps.add(first_enteredOTP_resync); otps.add(second_enteredOTP_resync); try { library.resynchronization(serial, otps); return STATE_AUTH; } catch (IOException e) {
debug.message("IambOOTP:: (" +userName+ ") - Exception IO: " + e.getMessage() ); } catch (IambOOTPLibraryException e) {
debug.message("IambOOTP:: (" +userName+ ") - Exception IambOOTPLibraryException: " + e.getMessage()); } catch (BadServerResponseException e) {
debug.message("IambOOTP:: (" +userName+ ") - Exception BadServerResponseException: " + e.getMessage()); }
case STATE_ERROR: return STATE_ERROR; default:
throw new AuthLoginException("invalid state"); } }
@Override public Principal getPrincipal() { if (userUUID != null) {
userPrincipal = new SampleAuthPrincipal(userUUID); return userPrincipal;
}
if (userName != null) {
userPrincipal = new SampleAuthPrincipal(userName); return userPrincipal; } else { return null; } } }
1.2 SampleAuthPrincipal.java
/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. *
* Copyright (c) 2011 ForgeRock AS. All Rights Reserved *
* The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License.
*
* You can obtain a copy of the License at * http://forgerock.org/license/CDDLv1.0.html
* See the License for the specific language governing * permission and limitations under the License. *
* When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at http://forgerock.org/license/CDDLv1.0.html
* If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]" *
@Override
public boolean equals(Object o) { if (o == null) { return false; } if (this == o) { return true; }
if (!(o instanceof SampleAuthPrincipal)) { return false;
}
SampleAuthPrincipal that = (SampleAuthPrincipal) o; if (this.getName().equals(that.getName())) { return true; } return false; } /**
* Return a hash code for this <code> SampleAuthPrincipal </code>. *
* @return a hash code for this <code> SampleAuthPrincipal </code>. */
@Override
public int hashCode() { return name.hashCode(); } }
1.3 JdbcConnect.java
package com.forgerock.openam.iamboo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; public class JdbcConnect {private Connection connect = null; private Statement statement = null; private ResultSet resultSet = null; private Config cfg;
private String mDbUser,mDbHost,mDbPwd,mDBTableName,mDbName,ServerIP; private int ServerPort;
private String username;
public JdbcConnect(String path,String username) { cfg = new Config(path);
this.username=username; mDbUser= cfg.getProperty("mDbUser"); mDbPwd = cfg.getProperty("mDbPwd"); mDbHost= cfg.getProperty("mDbHost"); mDbName= cfg.getProperty("mDbName"); mDBTableName=cfg.getProperty("mDBTableName"); ServerPort= Integer.parseInt(cfg.getProperty("ServerPort")); ServerIP=cfg.getProperty("ServerIP"); }
public List<String> readDataBase() throws Exception { try {
// This will load the MySQL driver, each DB has its own driver Class.forName("com.mysql.jdbc.Driver");
String connectionString = "jdbc:mysql://"+mDbHost+"/"+mDbName+"?" +"user="+mDbUser+"&password="+mDbPwd;
// Setup the connection with the DB
connect = DriverManager.getConnection(connectionString); // Statements allow to issue SQL queries to the database statement = connect.createStatement();
// Result set get the result of the SQL query
resultSet = statement.executeQuery("select * from " +mDbName+"." +mDBTableName +" where username =’" + username + "’");
return writeResultSet(resultSet); } catch (Exception e) { throw e; } finally { close(); } }
private List<String> writeResultSet(ResultSet resultSet) throws SQLException { List<String> ret = new ArrayList<String>();
// ResultSet is initially before the first data set while (resultSet.next()) {
// It is possible to get the columns via name
// also possible to get the columns via the column number // which starts at 1
// e.g. resultSet.getSTring(2);
String user = resultSet.getString("username"); String serialID = resultSet.getString("serialID"); ret.add(user);
ret.add(serialID); }
return ret; }
// You need to close the resultSet private void close() {
try { if (resultSet != null) { resultSet.close(); } if (statement != null) { statement.close();
} if (connect != null) { connect.close(); } } catch (Exception e) { } }
public String getServerIP() { return this.ServerIP;
}
public int getServerPort() { return this.ServerPort; } }
1.4 SampleAuth.xml
<?xml version="1.0" encoding="UTF-8"?> <!--Copyright (c) 2008 Sun Microsystems, Inc. All rights reserved Use is subject to license terms.
-->
<!DOCTYPE ModuleProperties PUBLIC "=//iPlanet//Authentication Module Properties XML Interface 1.0 DTD//EN" "jar://com/sun/identity/authentication/Auth_Module_Properties.dtd"> <ModuleProperties moduleName="SampleAuth" version="1.0" >
<Callbacks length="0" order="1" timeout="600" header="#NOT SHOWN#" />
<Callbacks length="2" order="2" timeout="120" header="Iamboo Authentication" > <NameCallback echoPassword="false" >
<Prompt> Enter OTP Code: </Prompt> </NameCallback> <ConfirmationCallback> <OptionValues>
<OptionValue>
<Value> Submit OTP Code </Value> </OptionValue>
</OptionValues>
</ConfirmationCallback> </Callbacks>
<Callbacks length="3" order="3" timeout="120" header="Iamboo - must Resync" > <NameCallback echoPassword="false" >
<Prompt> Enter First OTP Code: </Prompt> </NameCallback>
<NameCallback echoPassword="false" > <Prompt> Enter Second OTP Code: </Prompt> </NameCallback>
<ConfirmationCallback> <OptionValues>
<Value> Submit OTP Code </Value> </OptionValue> </OptionValues> </ConfirmationCallback> </Callbacks> </ModuleProperties>
1.5 amAuthSampleAuth.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE ServicesConfiguration PUBLIC "=//iPlanet//Service Management Services (SMS) 1.0 DTD//EN" "jar://com/sun/identity/sm/sms.dtd">
<ServicesConfiguration>
<Service name="iPlanetAMAuthSampleAuthService" version="1.0"> <Schema serviceHierarchy="/DSAMEConfig/authentication/iPlanetAMAuthSampleAuthService" i18nFileName="amAuthSampleAuth" revisionNumber="10" i18nKey="sampleauth-service-description"> <Organization> <AttributeSchema name="iplanet-am-auth-sampleauth-auth-level" type="single" syntax="number_range" rangeStart="0" rangeEnd="2147483647" i18nKey="a500"> <DefaultValues> <Value>1</Value> </DefaultValues> </AttributeSchema> <AttributeSchema name="sampleauth-service-specific-attribute" type="single" syntax="string" validator="no" i18nKey="a501"> <DefaultValues> <Value></Value> </DefaultValues> </AttributeSchema> <SubSchema name="serverconfig" inheritance="multiple"> <AttributeSchema name="iplanet-am-auth-sampleauth-auth-level" type="single" syntax="number_range" rangeStart="0" rangeEnd="2147483647" i18nKey="a500"> <DefaultValues> <Value>1</Value> </DefaultValues>
</AttributeSchema> <AttributeSchema name="sampleauth-service-specific-attribute" type="single" syntax="string" validator="no" i18nKey="a501"> <DefaultValues> <Value></Value> </DefaultValues> </AttributeSchema> </SubSchema> </Organization> </Schema> </Service> </ServicesConfiguration>
1.6 amAuthSampleAuth.properties
sampleauth-service-description=IambooOTP a500=Authentication Levela501=Service Specific Attribute sampleauth-ui-login-header=Login
sampleauth-ui-username-prompt=User Name: sampleauth-ui-password-prompt=Password:
sampleauth-error-1=Error 1 occured during the authentication sampleauth-error-2=Error 2 occured during the authentication
1.7 Config.cfg
#mySQL parameters mDbUser = **** mDbHost = **** mDbPwd = **** mDbName = **** mDBTableName=**** #Server parameters ServerPort=**** ServerIP=**** \end {verbatim} } \subsection{Config.java} {\scriptsize { \begin{verbatim} package com.forgerock.openam.iamboo; import java.io.FileInputStream; import java.io.InputStream;import java.util.Properties; public class Config {
private String path_config_file; Properties configFile;
public Config(String pcf) { this.path_config_file=pcf;
configFile = new java.util.Properties(); try {
InputStream is = new FileInputStream(path_config_file); configFile.load(is);
}catch(Exception eta){ eta.printStackTrace(); }
}
public String getProperty(String key) { String value = this.configFile.getProperty(key); return value; } }
2 SugarCRM
2.1 Login.php
<?phpini_set ("display_errors", "1"); error_reporting(E_ALL);
if(!defined(’sugarEntry’)||!sugarEntry) die(’Not A Valid Entry Point’);
/********************************************************************************* * SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc. *
* This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the * Free Software Foundation with the addition of the following permission added * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details.
*
* You should have received a copy of the GNU General Public License along with * this program; if not, see http://www.gnu.org/licenses or write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road, * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
*
* The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "Powered by * SugarCRM" logo. If the display of the logo is not reasonably feasible for * technical reasons, the Appropriate Legal Notices must display the words * "Powered by SugarCRM".
********************************************************************************/ /*********************************************************************************
* Description: TODO: To be written.
* Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. * All Rights Reserved.
* Contributor(s): ______________________________________..
********************************************************************************/ require_once(’/opt/sugarcrm/htdocs/simplesamlphp/lib/_autoload.php’);
$as = new SimpleSAML_Auth_Simple(’default-sp’); function error_message () { echo "<html> <head><title></title>