xPage => J2EE

Domino-Designer

Людям надо поморгать!
Lotus Team
06.12.2011
617
223
BIT
24
На мой взгляд интересное от Jesse Gallagher:

Если коротко: Попытка окунуть сообщество xPage в JEE.
 
  • Нравится
Реакции: lmike

garrick

Lotus Team
26.10.2009
1 367
152
BIT
353
Попытка окунуть сообщество xPage в JEE.
Так оно и есть JEE(JSF), только старое как окаменевший кал мамонта. Современный JSF и пр. уже далеко ушел от того, на котором основывалось XPages. Догонять надо, да никто, похоже, не собирается.
 

savl

Lotus Team
28.10.2011
2 624
314
BIT
525
мне интересно, тоже читал... сейчас думаю как из J2EE подключиться в Domino: DAS и/или прямое обращение...
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
мне интересно, тоже читал... сейчас думаю как из J2EE подключиться в Domino: DAS и/или прямое обращение...
DAS норм, есть особенности:
-это хттп и нужны авторизационные куки (если сешн авторизация)
-получение энтрисов и прочая (коллекций) завязано на параметр &Count, надо проверять - что получил по ключу
-если есть фронт - учитывать редайректы (описывал в унификации разработки)
-приятная особенность - PATCH
-json позволяет любую морду рисовать на JS
 

savl

Lotus Team
28.10.2011
2 624
314
BIT
525
DAS норм, есть особенности:
-это хттп и нужны авторизационные куки (если сешн авторизация)
-получение энтрисов и прочая (коллекций) завязано на параметр &Count, надо проверять - что получил по ключу
-если есть фронт - учитывать редайректы (описывал в унификации разработки)
-приятная особенность - PATCH
-json позволяет любую морду рисовать на JS

я так понимаю, что логиниться можно через URL names.nsf?login + параметрами логин/пароль, где-то я видел это... И надо SSO настраивать,чтобы между серверами проходило... И куки. Или нет?
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
я так понимаю, что логиниться можно через URL names.nsf?login + параметрами логин/пароль, где-то я видел это... И надо SSO настраивать,чтобы между серверами проходило... И куки. Или нет?
достаточно SSO и генерить куки
XML:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" rendered="false">
    <xp:this.beforePageLoad><![CDATA[#{javascript:try {
 var url = context.getUrl();
 var host = url.getHost();
 var domain=host.match(/\..*/)[0];
 var response=facesContext.getExternalContext().getResponse();
 
 //var token=paramValues.get("token").toString();
 //var rUrl=paramValues.get("url").toString();
 var user_enc = context.getUrlParameter('id');
 print("::xAgent_token::id:" + user_enc);
 var user=decrypt(base64_decode(user_enc));
print("::xAgent_token::User:" + user);
/* var httpURL = database.getHttpURL();
 var dbUrl= httpURL.replace("http://", "https://").match(/(.+)\?/)[1];
 print("url:"+dbUrl);
*/
  var generated=LtpaGenerator(user);
 
 //var header="LtpaToken=" + token + "; domain=" + domain + "; path=/";

 //response.setHeader("Set-Cookie", header);
 //facesContext.getExternalContext().redirect(rUrl);
  var externalContext:javax.faces.context.ExternalContext = facesContext.getExternalContext();
  var response:javax.servlet.http.HttpServletResponse = externalContext.getResponse();
  response.setHeader("Cache-Control", "no-cache");
  response.setDateHeader("Expires", -1);
  var externalContext:javax.faces.context.ExternalContext = facesContext.getExternalContext();
  var response:javax.servlet.http.HttpServletResponse = externalContext.getResponse();
  response.setHeader("Cache-Control", "no-cache");
  response.setDateHeader("Expires", -1);
  var gVars=getVars();
  print("::xAgent_token::token:"+generated);
  response.setHeader("Set-Cookie", "LtpaToken=" + generated + "; domain="+ gVars.domain + "; path=/");
  //response.setContentType("text/html;charset=UTF-8");
  var writer:java.io.PrintWriter = response.getWriter();
  writer.write(generated);
 
 } catch(e) {
        _dump(e);
 }
 }]]></xp:this.beforePageLoad>

    <xp:this.resources>
        <xp:script src="/SSJSUtils.jss" clientSide="false"></xp:script>
    </xp:this.resources>
</xp:view>
в SSJSUtils.jss
JavaScript:
function LtpaGenerator(userDN){
  importPackage(lmike.org);
  try{
    var ltpa:LtpaGenerator1=new LtpaGenerator1();
    if (arguments.length > 1){
      //print("::LtpaGenerator::call Init with:"+arguments[1])
      ltpa.initByConfiguration(sessionAsSigner,arguments[1]);
    }else{
      //print("::LtpaGenerator::call Init");
      ltpa.initByConfiguration(sessionAsSigner);
    }
    //print("::LtpaGenerator::userDN"+userDN);
    var token=ltpa.generateLtpaToken(userDN);
    return token;
  }catch(e){
    return errAttr+e.toString();
  }
}
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
java код для генерации (вызывается из JS)
Java:
/**
* LtpaToken Generator V1.1
*
* This Java class generates a valid LtpaToken valid for any user name.
*
* To use it on SSJS:
* -------------------
*  importPackage(com.developi.openntf);
*  var ltpa:LtpaGenerator=new LtpaGenerator();
*  ltpa.initByConfiguration(sessionAsSigner, "Developi:LtpaToken");
*  token=ltpa.generateLtpaToken("CN=Serdar Basegmez/O=developi");
*
* To use the token (make sure replace '.developi.info' with your SSO domain):
* -------------------------------------------------------------------------
*  response=facesContext.getExternalContext().getResponse();
*  response.setHeader("Set-Cookie", "LtpaToken=" + token + "; domain=.developi.info; path=/");
* facesContext.getExternalContext().redirect(someUrl);
*
* 1. "Developi:LtpaToken" is the SSO configuration key. If you are using Internet site configuration,  it will be
*     "Organization:TokenName". Otherwise, it will be "TokenName" only. You may check "($WebSSOConfigs)"
*     view in the names.nsf database.
* 2. sessionAsSigner should be given as parameter to the initByConfiguration method.
* 3. The signer of the database design should be listed as 'Owner' or 'Administrator' in the SSO configuration.
* 4. Current version only supports Domino keys. Tokens imported from Websphere will not generate valid tokens.
*
* Important Note:
* You will see "LMBCS" encoding below. This is because of that Domino encodes user names in LMBCS charset.
* As long as you use standard ANSI characters, it's OK. However if you use other languages (like Turkish) in
* user names, it will be encoded in default charset (ISO-8859-1). Normally, Domino JVM does not support LMBCS
* encoding. So you have to install a supporting library. I have found ICU (International Components for Unicode) library.
* However, it cannot be attached into NSF. So you have to install it into Domino JVM. To do this;
*
*  - Go to ICU Project site (http://www.icu-project.org)
*  - Download "icu4j-49_1.jar" and "icu4j-charset-49_1.jar" (or latest versions)
*  - Put those files into "{Domino Program Folder}\jvm\lib\ext"
*  - Restart your HTTP task
*
* This will install ICU library into your server. This library is licensed under X-License and can be used commercially.
* I didn't try but it can also be installed via OSGi plugin. Let me know if you do it :)
* Direct link for download: http://apps.icu-project.org/icu-jsp/downloadPage.jsp?ver=49.1&base=j&svn=release-49-1
*
*/

package lmike.org;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
//import java.util.Calendar;
//import java.util.Date;
import java.util.GregorianCalendar;

import javax.xml.bind.DatatypeConverter;

import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.NotesException;
import lotus.domino.Session;
import lotus.domino.View;

/*
* @author Serdar Basegmez, Developi (http://lotusnotus.com/en)
*/

public class LtpaGenerator1{
 
  public static final String NAMESDB="names.nsf";
  public static final String SSOVIEW="($WebSSOConfigs)";
  public static final String SSO_DOMINO_SECRETFIELD="LTPA_DominoSecret";
  public static final String SSO_DOMINO_DURATIONFIELD="LTPA_TokenExpiration";
  private String WebSSOSite="CRUINTERNET:CISsite";
  private boolean ready=false;

  private int duration=300;
  private String ltpaSecret="";
  private String classID;

  public LtpaGenerator1(){
      classID="::"+this.getClass().getName()+"::";
      System.out.println("constructor:" + this.getClass().getName());
  }


  public LtpaGenerator1(String ltpaSecret) {
    this();
    setLtpaSecret(ltpaSecret);
  }

  public LtpaGenerator1(String ltpaSecret, int duration) {
    this();
    setLtpaSecret(ltpaSecret);
    setDuration(duration);
  }

  public void initByConfiguration(Session session) throws Exception{
    initByConfiguration(session, WebSSOSite);
  }

  public void initByConfiguration(Session session, String configName) throws Exception {
    Database dbNames=null;
    View ssoView=null;
    Document ssoDoc=null;
    String memberID=classID+"initByConfiguration::";
    String status="try...";
    try {
/*      System.out.println(memberID+"with:"+configName);
      if (session==null)throw new Exception("session is null");
      Database db=session.getCurrentDatabase();
      if (db==null)throw new Exception("current db is null");
      db.recycle();
*/  
      status="get server name";
      String currentServer=session.getServerName();//db.getServer();
      status="get NAB from:"+currentServer;
      dbNames=session.getDatabase(currentServer, NAMESDB, false);
      status="get view"+SSOVIEW;
      ssoView=dbNames.getView(SSOVIEW);
      status="get config doc:"+configName;
      ssoDoc=ssoView.getDocumentByKey(configName, true);
      if(ssoDoc==null) {
        status="Unable to find SSO configuration with the given configName.";
        throw new IllegalArgumentException(status);
      }
     
      setLtpaSecret(ssoDoc.getItemValueString(SSO_DOMINO_SECRETFIELD));
      setDuration(ssoDoc.getItemValueInteger(SSO_DOMINO_DURATIONFIELD));
      status="success";
     
    } catch (NotesException ex) {
      status = "Notes exception:"+ex.text;
      throw new Exception("Notes Error: "+ex);
    } finally {
      try {
       
        if(dbNames!=null) dbNames.recycle();
        if(ssoView!=null) ssoView.recycle();
        if(ssoDoc!=null) ssoDoc.recycle();
        System.out.println(memberID+status);
      } catch(NotesException exc) {
        System.out.println(memberID+"finally catch:"+status);
        //ignore
      }
    }
  }
 
  public String generateLtpaToken(String userName) {
    String memberID=classID+"generateLtpaToken";
    if(!isReady()) {
      throw new IllegalStateException("LtpaGenerator is not ready.");
    }
   
    MessageDigest sha1 = null;
    //Calendar ci=Calendar.getInstance();
    GregorianCalendar creationDate=new GregorianCalendar();
    GregorianCalendar expiringDate=new GregorianCalendar();

    byte[] userNameArray=userName.getBytes();
   
    expiringDate.add(GregorianCalendar.MINUTE, duration);
   
    System.out.println(memberID+"Ltpa creation:"+creationDate.getTime().toString());
    System.out.println(memberID+"Ltpa expiring:"+expiringDate.getTime().toString());

    try {
      sha1 = MessageDigest.getInstance( "SHA-1" );
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace(System.err);
    }
       
    byte[] secretDecoded=DatatypeConverter.parseBase64Binary(ltpaSecret);

    // Look at important notes above...
    try {
      if(Charset.isSupported("LMBCS")) {
        userNameArray=userName.getBytes("LMBCS");
      }
    } catch (UnsupportedEncodingException e) {
      // Not supposed to fall here.
    }
     
    byte[] tokenBase=concatBytes(("\000\001\002\003"+getHexRep(creationDate)+getHexRep(expiringDate)).getBytes(), userNameArray);
   
    byte[] digest=sha1.digest(concatBytes(tokenBase, secretDecoded));
 
    return DatatypeConverter.printBase64Binary(concatBytes(tokenBase, digest));
  }



  public static byte[] concatBytes(byte[] arr1, byte[] arr2) {
    byte[] result=Arrays.copyOf(arr1, arr1.length+arr2.length);
    System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
    return result;
  }
 
  public static String getHexRep(GregorianCalendar date) {
    int timeVal=(int)(date.getTimeInMillis()/1000);
    String hex=Integer.toHexString(timeVal).toUpperCase();
   
    if(hex.length()>=8) {
      return hex;
    } else {
      return String.format("%0"+(8-hex.length())+"d", 0)+hex;
    }
  }

  public void setDuration(int duration) {
    this.duration = duration;
  }

  public void setLtpaSecret(String ltpaSecret) {
    this.ltpaSecret = ltpaSecret;
    this.ready=true;
  }

  public boolean isReady() {
    return ready;
  }

  public String test(String param){
      return "test:" + param;
  }
  public static void main(String[] args) {
      // TODO Auto-generated method stub

  }

}
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
private String WebSSOSite="CRUINTERNET:CISsite"; //установить свое значение
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
вызов из внешнего кода, делаем класс
Java:
public class AuthToken {
    static final String key=<Мой супер секрет>;//шифруем имя этим ключем, xAgent расшифровывает им (берет из базы)
    static final String userName=<Имя пользователя для которого генерим>;

    static final String dominoHost=<FQDN сервера>;
    static final String serverURL="https://"+dominoHost;
    static final String dbPart="/"<Путь к базе от notesdata>;
    static final String dasPart="/api/data/collections/name/";
    private static final String authPage="/xAgent_token.xsp?id=";
    static final String target=serverURL+dbPart+authPage;
    static{TrustAllCertificates.install();}//для доверия всем https
}
инициализируем куки
Java:
import com.ibm.commons.util.io.json.JsonObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.http.*;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URI;

import static com.setralubs.AuthToken.*;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;

public abstract class DASUtilsBase {
    static final org.slf4j.Logger LOG = LoggerFactory.getLogger(new Throwable().getStackTrace()[0].getClassName());
    public static final String API_BASE_URI = "/api";
    public static final String API_DATABASES_URI = "/api/data";
    public static final String API_VIEWS_URI = "/api/data/collections";
    public static final String API_DOCUMENTS_URI = "/api/data/documents";
    // argument prefixes
    public static final String SERVER_ARG = "server=";
    public static final String DATABASE_ARG = "database=";
    public static final String USERNAME_ARG = "user=";
    public static final String PASSWORD_ARG = "password=";
    public static final String RESOURCES_ARG = "resources=";
    public static final String VIEWNAME_ARG = "view=";
    static String BaseURI = "https://";
    static String databaseName = dbPart;
    static String dominoServer=dominoHost;
    static String viewName = "All";//"AllNames";
    static String resourcesArg = "";
    final JWT jwt;
    CloseableHttpClient client = null;

    public DASUtilsBase() {
        jwt = new JWT();
        String encBase64=new String(Base64.encodeBase64(jwt.encrypt(userName, key).getBytes()));
        RequestConfig customizedRequestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.BROWSER_COMPATIBILITY).build();
        HttpClientBuilder customizedClientBuilder = HttpClients.custom().setDefaultRequestConfig(customizedRequestConfig);
        //https://stackoverflow.com/a/40982127 redirect strategy
        customizedClientBuilder.setRedirectStrategy(new DefaultRedirectStrategy() {
            public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
                Args.notNull(request, "HTTP request");
                Args.notNull(response, "HTTP response");
                int statusCode = response.getStatusLine().getStatusCode();
                switch(statusCode) {
                    case 301:
                    case 307:
                    case 302:
                    case 308:
                    case 303:
                        return true;
                    case 304:
                    case 305:
                    case 306:
                    default:
                        return false;
                }
            }

            public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException, ProtocolException {
                URI uri = this.getLocationURI(request, response, context);
                String method = request.getRequestLine().getMethod();
                if(method.equalsIgnoreCase("HEAD")) {
                    return new HttpHead(uri);
                } else if(method.equalsIgnoreCase("GET")) {
                    return new HttpGet(uri);
                } else {
                    int status = response.getStatusLine().getStatusCode();
                    HttpUriRequest toReturn = null;
                    if(status == 301 || status == 307 || status == 308) {
                        toReturn = RequestBuilder.copy(request).setUri(uri).build();
                        toReturn.removeHeaders("Content-Length"); //Workaround for an apparent bug in HttpClient
                    } else {
                        toReturn = new HttpGet(uri);
                    }
                    return toReturn;
                }
            }
        });
        // Create the http client
        client = customizedClientBuilder.build();
        //set Authorization cookie
        try {
            HttpGet get = new HttpGet(target+encBase64);
            HttpResponse response=client.execute(get);
            printResponseBodyToConsole(response);
        } catch (IOException ioe) {
            LOG.error("Unable to connect to '" + serverURL + "'");
            LOG.error(ioe.getMessage());
            System.exit(-1);
        }
    }
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
после всего описанного - спокойно делаем запросы, код DASUtilsBase включает обработку редайректов от фронта
код для шифровки/расшифровки/подписи (JWT)
Java:
//https://connect2id.com/products/nimbus-jose-jwt/examples/jwt-with-hmac
        import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

//import javax.crypto.KeyGenerator;
//import javax.crypto.SecretKey;

public class JWT {
    public String sign(String payload, String secret) {
        JWSSigner signer=null;
        String ret="";

        try {
//https://stackoverflow.com/questions/41882474/java-pad-a-string-with-ascii-0-null-to-a-multiple-of-16-bytes
            byte[] plainBytes = secret.getBytes("UTF-8");
            byte[] paddedBytes = new byte[32];
            System.arraycopy(plainBytes, 0, paddedBytes, 0, plainBytes.length);
            signer = new MACSigner(paddedBytes);

            // Prepare JWT with claims set
            JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
                    .subject(payload)
                    //    .issuer("https://c2id.com")
                    //    .expirationTime(new Date(new Date().getTime() + 60 * 1000))
                    .build();

            SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);

            // Apply the HMAC protection
            signedJWT.sign(signer);

            // Serialize to compact form, produces something like
            // eyJhbGciOiJIUzI1NiJ9.SGVsbG8sIHdvcmxkIQ.onO9Ihudz3WkiauDO2Uhyuz0Y18UASXlSc1eS0NkWyA
            ret = signedJWT.serialize();
            System.out.println("::"+this.getClass().getName()+"::signed:"+ret);
        } catch (KeyLengthException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JOSEException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return ret;
    }
    public String encrypt(String text, String key){
        //String text = "Hello World";
        //String key = "Bar12345Bar12345"; // 128 bit key
        // Create key and cipher
        Key aesKey = null;
        byte[] plainBytes = null;
        try {
            plainBytes = key.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }
        byte[] paddedBytes = new byte[16];
        System.arraycopy(plainBytes, 0, paddedBytes, 0, plainBytes.length);
        aesKey = new SecretKeySpec(paddedBytes, "AES");
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES");
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // encrypt the text
        try {
            cipher.init(Cipher.ENCRYPT_MODE, aesKey);
        } catch (InvalidKeyException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        byte[] encrypted = null;
        try {
            encrypted = cipher.doFinal(text.getBytes("UTF-8"));
        } catch (IllegalBlockSizeException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BadPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("::"+this.getClass().getName()+"::encrypted size:"+encrypted.length);
        return DatatypeConverter.printBase64Binary(encrypted);
        //return new String(encrypted);
    }

    public String decrypt(String text, String key){
        // decrypt the text
        Key aesKey = null;
        byte[] plainBytes=null;
        try {
            plainBytes = key.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }
        byte[] paddedBytes = new byte[16];
        System.arraycopy(plainBytes, 0, paddedBytes, 0, plainBytes.length);
        aesKey = new SecretKeySpec(paddedBytes, "AES");
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES");
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            cipher.init(Cipher.DECRYPT_MODE, aesKey);
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
        String decrypted = null;
        try {
            System.out.println("::"+this.getClass().getName()+"::encrypted b64 size:"+DatatypeConverter.parseBase64Binary(text).length);
            decrypted = new String(cipher.doFinal(DatatypeConverter.parseBase64Binary(text)));
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        //System.err.println(decrypted);
        return decrypted;
    }
    public static void main(String args[]){
        JWT jwt=new JWT();
        String key=<Мой супер ключ>;
        System.out.println(jwt.decrypt(jwt.encrypt("test", key),key));
    }
}
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
не влезло в первую часть SSJSUtils, здесь из БД берем секрет, кот. используется для шифровки/расшифровки, importPackage(lmike.org); это пакадж java (код выше)
JavaScript:
function secretEXT(){
  var db:NotesDatabase=sessionAsSigner.getDatabase(session.getServerName(), session.getCurrentDatabase().getFilePath());
  var NV:NotesView=db.getView(OPTIONS);
  var doc:NotesDocument=NV.getDocumentByKey(OPT_SECRET);
//  print("optSecret UNID:"+doc.getUniversalID())
  secret=doc.getItemValueString(SECRETEXT_FLD);
  doc.recycle();NV.recycle();db.recycle();
  //print("::secretEXT::secret:\""+secret+"\"");
  return secret;
}

function encrypt(text){
  importPackage(lmike.org);
  try{
    var jwt:JWT=new JWT();
    return jwt.encrypt(text, secretEXT());
  }catch(e){
    _dump(e);
  }

}
function decrypt(text){
  importPackage(lmike.org);
  try{
    var jwt:JWT=new JWT();
    return jwt.decrypt(text, secretEXT());
  }catch(e){
    _dump(e);
  }
}
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
@lmike
Круть, спасибо, а если без xpage?
надо чтобы сервер куки устанавливал, агент врядли сможет, сервлет писать - это практически хэпага ;)
только часть схемы, да еще и без шифрования
лепить куки самому, в запрос...можно, но лучше (правильнее) - когда все делает редайрект (т.е. куки ставит сервер в ответе, а клиент их сохраняет и использует)
в варианте без апачевской либы я делал с передачей полученных кукисов
Java:
    public void authorize() throws IOException {
        cookies=null;
        URL url=new URL(target+encBase64);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        if (conn.getResponseCode() == 200) {
            //read response from authPage
            result2String(conn);
            cookies = conn.getHeaderFields().get("Set-Cookie");
/*
            for (String el : cookies) {
                System.out.println(el);
            }
*/
        }
    }
private String result2String(HttpURLConnection conn) throws IOException {
    StringBuilder res = new StringBuilder();
    BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    String line = "";
    while ((line = reader.readLine()) != null) {
        //System.out.println(line);
        res.append(line);
    }
    reader.close();
    return res.toString();
}

....
public InputStream request2URL(String target) throws IOException {

    URL url=new URL(target);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    if (cookies!=null)
        for (String cookie : cookies) {
            conn.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
        }

    InputStream res = null;
    if (conn.getResponseCode() == 200) {
        //System.out.println("OK");
        res= conn.getInputStream();
        if (cookies==null)cookies = conn.getHeaderFields().get("Set-Cookie");
    }else{
        System.out.format("Request status %d -> %s\n",conn.getResponseCode(),conn.getResponseMessage());
    }
    return res;
}
тоже работает, но вызывал туже хэпагу (т.е. "сервер считает" что выдал токен)
 
Последнее редактирование:

savl

Lotus Team
28.10.2011
2 624
314
BIT
525
надо чтобы сервер куки устанавливал, агент врядли сможет, сервлет писать - это практически хэпага ;)

а так?

&redirectto=/dbname.nsf/api/data/collections/name/<viewName>

или если обернуть в пост:

POST HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Username=ME&Password=Secret&redirectto=/dbname.nsf/api/data/collections/name/<viewName>
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
а так?

&redirectto=/dbname.nsf/api/data/collections/name/<viewName>

или если обернуть в пост:

POST HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Username=ME&Password=Secret&redirectto=/dbname.nsf/api/data/collections/name/<viewName>
не уверен в устойчивости работы, ща посмотрю...
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
не уверен в устойчивости работы, ща посмотрю...
будет работать и без редайрект (redirectto)
но ведь токен будет получен в любом случае
и генерация токена позволяет не знать пароль пользователя, что упрощает тестирование под любым аккаунтом ;)
т.е. есть мастерпароль и этого достаточно (хотя опаснее с т.з. сесуриту)
+получаем возможность сделать (использовать) произвольную аутентификацию

для прохода с паролем достаточно (в базовом классе)поменять:
Java:
//HttpGet get = new HttpGet(target+encBase64);
HttpGet get = new HttpGet(BaseURI + dominoServer+"/names.nsf?Login&Username=Super%20User&Password=mypassword");
 
Последнее редактирование:

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
полный код базового класса
Java:
import com.ibm.commons.util.io.json.JsonObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.http.*;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URI;

import static com.setralubs.AuthToken.*;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;

public abstract class DASUtilsBase {
    static final org.slf4j.Logger LOG = LoggerFactory.getLogger(new Throwable().getStackTrace()[0].getClassName());
    public static final String API_BASE_URI = "/api";
    public static final String API_DATABASES_URI = "/api/data";
    public static final String API_VIEWS_URI = "/api/data/collections";
    public static final String API_DOCUMENTS_URI = "/api/data/documents";
    // argument prefixes
    public static final String SERVER_ARG = "server=";
    public static final String DATABASE_ARG = "database=";
    public static final String USERNAME_ARG = "user=";
    public static final String PASSWORD_ARG = "password=";
    public static final String RESOURCES_ARG = "resources=";
    public static final String VIEWNAME_ARG = "view=";
    static String BaseURI = "https://";
    static String databaseName = dbPart;
    static String dominoServer=dominoHost;
    static String viewName = "All";//"AllNames";
    static String resourcesArg = "";
    final JWT jwt;
    CloseableHttpClient client = null;

    public DASUtilsBase() {
        jwt = new JWT();
        String encBase64=new String(Base64.encodeBase64(jwt.encrypt(userName, key).getBytes()));
        RequestConfig customizedRequestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.BROWSER_COMPATIBILITY).build();
        HttpClientBuilder customizedClientBuilder = HttpClients.custom().setDefaultRequestConfig(customizedRequestConfig);
        //https://stackoverflow.com/a/40982127 redirect strategy
        customizedClientBuilder.setRedirectStrategy(new DefaultRedirectStrategy() {
            public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
                Args.notNull(request, "HTTP request");
                Args.notNull(response, "HTTP response");
                int statusCode = response.getStatusLine().getStatusCode();
                switch(statusCode) {
                    case 301:
                    case 307:
                    case 302:
                    case 308:
                    case 303:
                        return true;
                    case 304:
                    case 305:
                    case 306:
                    default:
                        return false;
                }
            }

            public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException, ProtocolException {
                URI uri = this.getLocationURI(request, response, context);
                String method = request.getRequestLine().getMethod();
                if(method.equalsIgnoreCase("HEAD")) {
                    return new HttpHead(uri);
                } else if(method.equalsIgnoreCase("GET")) {
                    return new HttpGet(uri);
                } else {
                    int status = response.getStatusLine().getStatusCode();
                    HttpUriRequest toReturn = null;
                    if(status == 301 || status == 307 || status == 308) {
                        toReturn = RequestBuilder.copy(request).setUri(uri).build();
                        toReturn.removeHeaders("Content-Length"); //Workaround for an apparent bug in HttpClient
                    } else {
                        toReturn = new HttpGet(uri);
                    }
                    return toReturn;
                }
            }
        });
        // Create the http client
        client = customizedClientBuilder.build();
        //set Authorization cookie
        try {
            HttpGet get = new HttpGet(target+encBase64);
            //HttpGet get = new HttpGet(BaseURI + dominoServer+"/names.nsf?Login&Username=Super%20User&Password=mypassword");
            HttpResponse response=client.execute(get);
            printResponseBodyToConsole(response);
        } catch (IOException ioe) {
            LOG.error("Unable to connect to '" + serverURL + "'");
            LOG.error(ioe.getMessage());
            System.exit(-1);
        }
    }

    static String getDatabaseName() {
        return databaseName;
    }

    protected abstract JsonObject getDefaultItems();

    protected abstract String getForm();


    HttpEntity entityFromJson(JsonObject jsonItem) {
        return new StringEntity(jsonItem.toString(),
                APPLICATION_JSON);
    }

    HttpEntity entityFromJson(String jsonString) {
        return new StringEntity("{"+jsonString+"}",
                APPLICATION_JSON);
    }

    /*
     *  Use org.apache.commons.httpclient.methods.PostMethod to create a document.
     *  The new document URI is returned in the Location response-header field.
     */
    String createDocument(String url, JsonObject jsonItem) throws IOException {
        String NewDocURI = null;
        url=url+"?"+"computewithform=true"+"&form="+getForm();
        HttpPost post = new HttpPost(url);
        post.setEntity(
                new StringEntity(jsonItem.toString(),
                        APPLICATION_JSON));
        HttpResponse response = client.execute(post);
        if (response.getStatusLine().getStatusCode() != 201) {
            LOG.error("Return code: " + response.getStatusLine().getStatusCode()
                    + " on POST of url: " + url);
            return null;
        }

        Header newLoc = response.getFirstHeader("Location");
        NewDocURI = newLoc.getValue();
        printResponseBodyToConsole(response);
        return NewDocURI;
    }


    /*
     * Use org.apache.commons.httpclient.methods.GetMethod to read a document.
     */
    HttpResponse readDocument(String url) throws IOException {
        HttpResponse response=null;
        HttpGet get = new HttpGet(url);
        get.addHeader("Content-Type", "application/json");
        get.addHeader("Accept", "application/json");
        response=client.execute(get);
        return response;
    }

    String createBodyContent(String base64content, String attachName){
        String fileSuffix = attachName.substring(attachName.lastIndexOf(".")+1);
        return "  \"Body\": {" +
                "    \"type\": \"multipart\"," +
                "    \"content\": [" +
                "      {" +
                "        \"contentType\": \""+MIMEType.get(fileSuffix)+"; name=\\\""+attachName+"\\\"\"," +
                "        \"contentDisposition\": \"attachment; filename=\\\""+attachName+"\\\"\"," +
                "        \"contentTransferEncoding\": \"base64\",\n" +
                "        \"data\": \""+base64content+"\"" +
                "      }" +
                "    ]" +
                "  }";
    }

    String createBodyContent(File file, String attachName) throws FileNotFoundException {
        InputStream is=new FileInputStream(file);
        String ret=null;
        //byte[] bytes=new byte[(int)file.length()];
        try {
            byte[] bytes = IOUtils.toByteArray(is);
            ret=createBodyContent(Base64.encodeBase64String(bytes),attachName);
        } catch (IOException e) {
            LOG.error(e.getMessage());
            //e.printStackTrace();
        }
        return ret;
    }

    private void __updateDocument(HttpPatch put) throws IOException {
        put.addHeader("Content-Type", "application/json");
        put.addHeader("Accept", "application/json");
        //put.addHeader("X-HTTP-Method-Override", "PATCH");

        HttpResponse response = client.execute(put);
        if (response.getStatusLine().getStatusCode() != 200) {
            LOG.error("Return code: " + response.getStatusLine().getStatusCode()
                    + "\nReason:"+response.getStatusLine().getReasonPhrase()
                    + "\n on PUT of uri: " + put.getURI());
            return;
        }
        LOG.debug("success PUT");
        printResponseBodyToConsole(response);
        LOG.debug("END PUT");

    }

    void updateDocument(String url, JsonObject jsonItem) throws IOException {
        //url=url.replaceAll("http://","https://").replaceFirst(":[0-9]+","");
        HttpPatch put = new HttpPatch(url);
        put.setEntity(entityFromJson(jsonItem));
        __updateDocument(put);
    }

    void updateDocument(String url, String jsonString) throws IOException{
        //url=url.replaceAll("http://","https://").replaceFirst(":[0-9]+","");
        HttpPatch put = new HttpPatch(url);
        String s="{\n"+ jsonString+"}";
        put.setEntity(new StringEntity(s));
        __updateDocument(put);
    }

    /*
     * Use org.apache.commons.httpclient.methods.DeleteMethod to delete a document.
     */
    void deleteDocument(String url) throws IOException {
        HttpDelete delete = null;
        //url=url.replaceAll("http://","https://").replaceFirst(":[0-9]+","");
        delete = new HttpDelete(url);
        delete.addHeader("Content-Type", "application/json");
        delete.addHeader("Accept", "application/json");
        HttpResponse response = client.execute(delete);
        printResponseBodyToConsole(response);
        if (response.getStatusLine().getStatusCode() != 200) {
            LOG.debug("Error: Return code "
                    + response.getStatusLine().getStatusCode() + " with DELETE on url: "
                    + url);
        }
    }

    /*
     * Print response body to standard out
     */
    void printResponseBodyToConsole(HttpResponse response) {
        try {
            LOG.debug("Result:"+response.getStatusLine().getReasonPhrase());
            LOG.debug("Response:\n"+org.apache.http.util.EntityUtils.toString(response.getEntity()));
        } catch (IOException ioe) {
            LOG.error("Unable to connect to '" + dominoServer
                    + "'");
            LOG.error(ioe.getMessage());
        }
    }
}
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
467
производный класс для тестирования
Java:
import com.ibm.commons.util.io.json.JsonJavaObject;
import com.ibm.commons.util.io.json.JsonObject;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class DASUtilsTest extends DASUtilsBase {
    static final org.slf4j.Logger LOG = LoggerFactory.getLogger(new Throwable().getStackTrace()[0].getClassName());

    public DASUtilsTest(){
        super();
    }
    public static void main(String[] args) throws Exception {
        DASUtilsTest obj=new DASUtilsTest();
        BaseURI = BaseURI + dominoServer;
        obj.testDocuments(BaseURI + "/" + getDatabaseName() +  API_DOCUMENTS_URI);
    }

    /*
     * Get list of views and folders of a database
     */
    private void testViews(String url) {
        HttpGet get = null;
        try {
            LOG.debug("");
            LOG.debug("Test of views resource..\n");

            get = new HttpGet(url);
            HttpResponse response=client.execute(get);
            printResponseBodyToConsole(response);
            LOG.debug("End test of views resource..\n\n");
        } catch (IOException ioe) {
            LOG.error("Unable to connect to '" + dominoServer + "'");
            LOG.error(ioe.getMessage());
            System.exit(-1);
        }
    }

    /*
     * Get list of entries in a given view
     */
    private void testViewEntries(String url) {
        HttpGet get = null;
        try {
            LOG.debug("");
            LOG.debug("Test of view entries resource..\n");

            get = new HttpGet(url);
            HttpResponse  response=client.execute(get);
            printResponseBodyToConsole(response);
            LOG.debug("End test of view entries resource..\n\n");
        } catch (IOException ioe) {
            LOG.error("Unable to connect to '" + dominoServer + "'");
            LOG.error(ioe.getMessage());
            System.exit(-1);
        }
    }

    /*
     * Perform REST style CRUD (Create, Read, Update, and Delete) operations on a single document.
     */
    private void testDocuments(String url) {
        LOG.debug("");
        LOG.debug("Test of documents resource..\n");

        // Create a new document in the database and return the URI of the document.
        String documentURI = null;
        HttpResponse  getResponse=null;
        try {
            documentURI=createDocument(url, getDefaultItems());
            // Read the new document
             getResponse = readDocument(documentURI);
        } catch (IOException ioe) {
            LOG.error("Unable to connect to '" + dominoServer
                    + "'");
            LOG.error(ioe.getMessage());
            System.exit(-1);
        }

        if (getResponse.getStatusLine().getStatusCode() == 200) {
            printResponseBodyToConsole(getResponse);
        } else {
            LOG.debug("Error: GET return code expected 200, actual "
                    + getResponse.getStatusLine().getStatusCode());
            System.exit(-1);
        }

        // Update the document, change City from "Quincy" to "Braintree".
        JsonObject jsonItem = new JsonJavaObject();
        jsonItem.putJsonProperty("TestField", "Test");
        try {
            updateDocument(documentURI, jsonItem);
            String s=
                    "\"City\": \"FuckBraintree\",\n"+
                    "  \"Subject\": \"Sample file attachment\",\n" +
                    createBodyContent(new File("add/image_1.jpg"), "image_1.jpg");//Images.testImage,"image.jpg")
            updateDocument(documentURI, s);
            s=createBodyContent(new File("add/image_1.jpg"), "imageXXX.jpg");
            updateDocument(documentURI, s);
            // Read the document after update.
            getResponse = readDocument(documentURI);
            if (getResponse.getStatusLine().getStatusCode() == 200) {
                //printResponseBodyToConsole(getResponse);
            } else {
                LOG.debug("Error: GET return code expected 200, actual "
                        + getResponse.getStatusLine().getStatusCode());
                System.exit(-1);
            }

            // Delete the document.
            deleteDocument(documentURI);

            // Attempt to read the document after delete, should be not found.
            getResponse = readDocument(documentURI);
            if (getResponse.getStatusLine().getStatusCode() == 404) {
                LOG.debug("Document not found, as expected.");
            } else {
                LOG.debug("Error: GET return code expected 404, actual "
                        + getResponse.getStatusLine().getStatusCode());
                System.exit(-1);
            }
        } catch (FileNotFoundException e) {
            LOG.error(e.getMessage());
            System.exit(-1);
        } catch (IOException e) {
            LOG.error("Unable to connect to '" + dominoServer
                    + "'");
            LOG.error(e.getMessage());
            System.exit(-1);
        }
    }

    @Override
    protected JsonObject getDefaultItems() {
        // Use JSON library methods to create a JSON object representing a new document.
        JsonObject jsonItem = new JsonJavaObject();
        jsonItem.putJsonProperty("FirstName", "Aaron");
        jsonItem.putJsonProperty("LastName", "Adams");
        jsonItem.putJsonProperty("EMail", "aaron_adams@renovations.com");
        jsonItem.putJsonProperty("City", "Quincy");
        jsonItem.putJsonProperty("State", "MA");
        jsonItem.putJsonProperty("Id", "CN=Aaron Adams/O=renovations");
        jsonItem.putJsonProperty("Form", "Contact");
        return jsonItem;
    }


    @Override
    protected String getForm() {
        return "batch";
    }
}
пропертя для логгера
Apache-конфиг:
log4j.logger.com.setralubs.DASUtilsTest=DEBUG, DAS
log4j.logger.com.setralubs.DASUtilsBase=DEBUG, DAS
log4j.logger.com.setralubs.DASUtilsBase$1=INFO, DAS
log4j.appender.DAS=org.apache.log4j.ConsoleAppender
log4j.appender.DAS.layout=org.apache.log4j.PatternLayout
log4j.appender.DAS.layout.ConversionPattern=[%5p] %d [%t] %X{file} %c{1}:%L - %m%n

#log4j.rootLogger=INFO, stdout
#log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#log4j.appender.stdout.Target=System.out
#log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
#[%5p] %d [%t] %X{file} %c{1} - %m%n
log4j.logger.net.sf.jsi=INFO, stdout
log4j.logger.org.apache.pdfbox=INFO, stdout
log4j.logger.org.apache.http=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!