Nexus maven repository with Apache as proxy and OpenDJ for authentication

For a client I have recently setup a nexus (oss) maven repository with apache proxy/reverse proxy and OpenDJ to provide LDAP authentication.

Nexus, which competes with Artifactory as maven repository, by default is waiting on port 8081 with the context path /nexus, which means that you can connect to it to the address http://localhost:8081/nexus (admin/admin123)

Having defined the name of my machine as maven I first configured Apache as proxy pointing to Nexus in this way:

ProxyPass         /nexus http://maven:8081/nexus
ProxyPassReverse  /nexus http://maven:8081/nexus

Having configured Apache to accept only https connections to be forwarded to Nexus in http (so https–>Apache–>http–>Nexus), I need to add before the ProxyPass directive the following RequestHeader:

RequestHeader set X-Forwarded-Proto “https”

in this way, the Nexus zip version, which is bundled with the servlet container Jetty 8.1.11 (which from the version 7.2.0 understands x-forwarded-proto header, see also the commit related) it will reply with the connection option (forwarded=true), see below the default configuration (in /nexus/config/jetty.xml):

<New class=org.sonatype.nexus.bootstrap.jetty.instrumentedSelectChannelConnector">
<Set name="host">${application-host}</Set>
<Set name="port>${application-port}</Set>
<Set name="forwarded">true</Set>
</New>

Now we add the ldap authentication, we go under Administration -> Server and add the OSS LDAP Authentication Realm to the Selected Realms.

I have OpenDJ listening on port 1389 (default), so we go under Security -> LDAP Configuration, we set the following parameters:

Connection
Protocol ldap
Hostname opendj
Port 1389
Search Base dc=my,dc=example,dc=com
Authentication
Authentication method Simple Authentication
Username cn=admin
Password mypassword
User Element Mapping
Base DN ou=users
Object Class inetOrgPerson
User ID Attribute uid
Real Name Attribute cn
E-Mail Attribute mail
Group Element Mapping
Group type Dynamic groups
Member of Attribute isMemberOf

The last 2 lines are optional to Static groups, but since OpenDJ allows to use the isMemberOf attribute, I preferred to use Dynamic groups (see Nexus documentation). For static groups you can check out also this article based on OpenDS on which OpenDJ is based on.

Now if you click on the Check User Mapping button you should see the list of users and groups retrieved from OpenDJ.

The last step would be add roles to the user so we go under Security ->Roles -> Add…->External Role Mapping and we select the LDAP group which will act as nexus administrators and we add all the roles for the group. So we logout and then we login with the nexus ldap administrator.

 

 

 

 

Advertisements

Estendere Selenium per validare pagine web, parte 2

Nel precedente articolo abbiamo visto come sia semplice fare delle chiamate XmlhttpRequest da Selenium testando il tutto sul css validator.

In un’ottica di automatizzare i test di selenium con Hudson (vedi plugin), potremmo pensare anche di salvare o di inviare per email il report generato da guardare in un secondo momento.

Per fare ciò invece di chiamare direttamente il validator potremmo chiamare un proxy scritto in PHP (o in un altro linguaggio) che:

  1. prende in input l’indirizzo della pagina da validare, l’indirizzo email e il tipo di validazione (CSS21, CSS3 ecc)
  2. chiama il validator e ottiene il report in xml
  3. salva il report in html e invia l’html per email (usando un file xslt per fare la trasformazione da xml a html)
  4. restituisca il report xml.

Andiamo a vedere dunque il comando da selenium:

storeLocation entry
checkCss ${entry} validity, email=emidiostani@gmail.com, profile=CSS21
verifyExpression ${validity} true

Come potete vedere una volta memorizzato l’indirizzo della pagina corrente nella variable entry, passiamo tale indirizzo al comando checkCss che restituisce il valore della variabile validity e prende come opzioni l’email e il profile (magari è meglio spostarli nella colonna di centro in futuro). Verifichiamo alla fine che il valore della variabile validity sia true.

Andiamo a vedere il codice (vi risparmio la funzione chiama WebService che abbiamo già visto nel precedente articolo):

Selenium.prototype.doCheckCss = function( uri, names ){

 var email = "";
 var profile = "";
 var array = names.split(',');
 for (var i = 0; i < array.length; i++){
 var name = array[i].trim();
 if(name.substr(0,5)=="email"){
 LOG.info( 'email = ' + name);
 var email_array = name.split('=');
 email = email_array[1];
 }
 else if(name.substr(0,7)=="profile"){
 LOG.info( 'profile = ' + name);
 var profile_array = name.split('=');
 profile = profile_array[1];
 }
 }

 var params = [
 {
 "param" : "uri",
 "value" : uri,
 },
 {
 "param" : "email",
 "value" : email,
 },
 {
 "param" : "profile",
 "value" : profile,
 },
 ];

 var lista = "";
 for(var i=0; i<params.length; i++){
 lista +="&" + params[i].param + "=" + encodeURIComponent(params[i].value);
 }

 var proxycss ="http://192.39.226.51/proxycss.php?";
 var indirizzo = proxycss+lista;
 LOG.info( 'indirizzo = ' + indirizzo);

 var responseXML = chiamaWebService(indirizzo);
 LOG.info( 'response = ' + responseXML);

 for (var i = 0; i < array.length; i++){
 var name = array[i].trim();
 if(name.substr(0,5)!="email" && name.substr(0,7)!="profile"){
 var name2 = "m:"+name;
 LOG.info( 'name2 = ' + name2);
 storedVars[name] = responseXML.getElementsByTagName(name2)[0].firstChild.nodeValue;
 LOG.info( 'callWebService: returned [' + storedVars[name] + ']' );
 }
 }
};

Nella prima parte estraiamo le opzioni email e profile da inserire insieme all’indirizzo (uri) nell’array params.

Concateniamo poi i parametri all’inidirizzo del proxy che contattiamo con la chiamaWebService (notate l’indirizzo ip che ovviamente dovete cambiare).

Estraiamo infine dalla risposta xml i parametri che abbiamo passato (ad eccezione di email e profile) da memorizzare nell’array associativo storedVars (predefinito per Selenium).

Ci resta da vedere il proxy php che dunque deve ricevere uri, email e profile e intrinsecamente:

  1. l’indirizzo della css validator (più altri parametri impliciti)
  2. il path del file xslt da applicare
  3. il path del report da salvare

Ed ecco il codice:

<?php

$uri = trim($_REQUEST['uri']);
$profile = trim(strtolower($_REQUEST['profile']));
$email = trim(strtolower($_REQUEST['email']));
$css = "http://192.39.226.51:8081/css-validator/validator?";
$usermedium = "all";
$warning = "1";
$lang = "en";
$output = "soap12";

if($profile =='')
 $profile="css21";
$address = $css."uri=".$uri."&profile=".$profile."&usermedium=".$usermedium."&warning=".$warning."&lang=".$lang."&output=".$output;
$result = file_get_contents($address);

$XSL = new DOMDocument();
$XSL->load( '/var/www/rest_style_css.xslt', LIBXML_NOCDATA);
$xslt = new XSLTProcessor();
$xslt->importStylesheet( $XSL );
$XML = new DOMDocument();
$XML->loadXML( $result );
$html = $xslt->transformToXML( $XML );

$dir = "reports/css/";
$report = "css-report-". date("H:i:s").".html";
$pathmyFile = "/var/www/".$dir.$report;
$fh = fopen($pathmyFile, 'w') or die("can't open file");
fwrite($fh, $html);
fclose($fh);

if($email !=''){
 $to = $email;
 $subject = 'CSS validation';
 $addresstomyfile = "<a href=http://".$_SERVER['SERVER_ADDR']."/".$dir.$report.">".$report."</a>";
 $message = "Report generated: ".$addresstomyfile." on ".date("j F, Y, g:i a")."\n".$html;
 $headers  = "From: Selenium Server<selenium.server@gmail.com>\r\n";
 $headers .= 'MIME-Version: 1.0' . "\r\n";
 $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
 mail($to, $subject, $message, $headers);
}

header('Content-type: text/xml');
echo $result;
?>

Si suppone dunque che:

  1. avete  la libreria xml e xsl per php (riavviate apache dopo averla installata)
  2. avete creato la cartella reports/css sotto la var/www
  3. avete creato un file xslt (che trovate alla fine dell’articolo) nella var/www
  4. abbiate una applicazione come sendmail che è in ascolto per mandare email

Il codice è facile da leggere:

  • una volta estratti i parametri li concateniamo per chiamare il validator con il metodo file_get_content(),
  • convertiamo il file xml ottenuto in html tramite il file xslt
  • salviamo tale file nella cartella reports/css
  • se l’email era stata passata come opzione allora mandiamo email l’html ottenuto
  • restituiamo (possiamo vedere anche tramite browser) il risultato xml

Potremmo in futuro passare solo una parte del risultato xml per risparmiare banda usata.

Ora potete fare lo stesso con w3c validator (su Ubuntu installate w3c-markup-validator), rss validator (ho testato in remoto ma non installato), l’achecker per l’accessibilità (che potete traquillamente installare (vi consiglio da svn).

File xslt da usare:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:m="http://www.w3.org/2005/07/css-validator" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="html" encoding="UTF-8"/>
 <xsl:template match="m:cssvalidationresponse">
 <html>
 <head>
 <title>CSS report</title>
 </head>
 <body>
 <table width="100%">
 <TR bgcolor="#f7f3fd">
 <TD colspan="6">Analyzed web page address:
 <a href="{m:uri}" onclick="popup('{m:uri}; return false;')" title="{m:uri}" target="_new">
 <xsl:value-of select="m:uri" />
 </a>
 </TD>
 </TR>
 <TR bgcolor="#f7f3fd">
 <TD colspan="3"><B>Errors</B></TD>
 </TR>
 <TR bgcolor="#f7f3fd">
 <TD><B>Line</B></TD>
 <TD><B>Error Type</B></TD>
 <TD><B>Context</B></TD>
 <TD><B>Error Subtype</B></TD>
 <TD><B>Skipped String</B></TD>
 <TD><B>Message</B></TD>
 </TR>
 <xsl:for-each select="m:result/m:errors/m:errorlist/m:error">
 <tr>
 <xsl:if test="(position() mod 2 = 1)">
 <xsl:attribute name="bgcolor">#EEEEFF</xsl:attribute>
 </xsl:if>
 <TD><xsl:value-of select="m:line" /></TD>
 <TD><xsl:value-of select="m:errortype" /></TD>
 <TD><xsl:value-of select="m:context" /></TD>
 <TD><xsl:value-of select="m:errorsubtype" /></TD>
 <TD><xsl:value-of select="m:skippedstring" /></TD>
 <TD><xsl:value-of select="m:message" /></TD>
 </tr>
 </xsl:for-each>
 </table>
 <br/>
 <table width="100%">
 <TR rowspan="4" bgcolor="#f7f3fd">
 <TD width="10%"><B>Warnings</B></TD>
 </TR>
 <TR bgcolor="#f7f3fd">
 <TD width="10%"><B>Line</B></TD>
 <TD width="10%"><B>Level</B></TD>
 <TD width="70%"><B>Message</B></TD>
 <TD width="10%"><B>Context</B></TD>
 </TR>
 <xsl:for-each select="m:result/m:warnings/m:warninglist/m:warning">
 <tr>
 <xsl:if test="(position() mod 2 = 1)">
 <xsl:attribute name="bgcolor">#EEEEFF</xsl:attribute>
 </xsl:if>
 <TD width="10%"><xsl:value-of select="m:line" /></TD>
 <TD width="10%"><xsl:value-of select="m:level" /></TD>
 <TD width="70%"><xsl:value-of select="m:message" /></TD>
 <TD width="10%"><xsl:value-of select="m:context" /></TD>
 </tr>
 </xsl:for-each>
 </table>
 </body>
 </html>
 </xsl:template>
</xsl:stylesheet>