sasmetadatasas-metadata

Creating Internal Accounts in SAS Metadata Server by programm on SAS Base


I'm trying to create Internal Accounts programmaticaly by using proc metadata. The code section below creates person with External Login.

put"<Person Name=%str(%')&&PersonName&i.%str(%')>";
   put"<Logins>";
      put"<Login Name=%str(%')Login.&&PersonName&i.%str(%')  Password=%str(%')&&word&i.%str(%')/>";
   put"</Logins>";
put"</Person>";

To create ExternalLogin we can set attribute Password, and in SAS Metadata it will be encrypted automaticaly. But to create InternalLogin type of object it is necessary to make the hash value of the password and the salt. I know that the standard sas002 encryption method, but in the case of using proc pwencode how to obtain the value of salt?

Is it possible create InternalLogin by using SAS Base?

Thanx.


Solution

  • So on. I found an article that can tell us how to create Stored Process for this problem. My answer is addition to the article.
    The approach is base on execute java methods from sas programm.

    1. Prerare setPasswd.java class

    I've modified class from article. Separate code to connect to metadata server and create InternalLogin

    import java.rmi.RemoteException;
    import com.sas.metadata.remote.AssociationList;
    import com.sas.metadata.remote.CMetadata;
    import com.sas.metadata.remote.Person;
    import com.sas.metadata.remote.MdException;
    import com.sas.metadata.remote.MdFactory;
    import com.sas.metadata.remote.MdFactoryImpl;
    import com.sas.metadata.remote.MdOMIUtil;
    import com.sas.metadata.remote.MdOMRConnection;
    import com.sas.metadata.remote.MdObjectStore;
    import com.sas.metadata.remote.MetadataObjects;
    import com.sas.metadata.remote.PrimaryType;
    import com.sas.metadata.remote.Tree;
    import com.sas.meta.SASOMI.ISecurity_1_1;
    import com.sas.iom.SASIOMDefs.VariableArray2dOfStringHolder;
    
    public class setPasswd {
      String serverName = null;
      String serverPort = null;
      String serverUser = null;
      String serverPass = null;
      MdOMRConnection connection = null;
      MdFactoryImpl _factory = null;
      ISecurity_1_1 iSecurity = null;
      MdObjectStore objectStore = null;
      Person person = null;
    
        public int connectToMetadata(String name, String port, String user, String pass){
        try {
                serverName = name;
              serverPort = port;
              serverUser = user;
              serverPass = pass;
          _factory = new MdFactoryImpl(false);
          connection = _factory.getConnection();
          connection.makeOMRConnection(serverName, serverPort, serverUser, serverPass);
          iSecurity = connection.MakeISecurityConnection();
          return 0;
    
        }catch(Exception e){
          return 1;
        }
        }
    
        public setPasswd(){};
    
        public int changePasswd(String IdentityName, String IdentityPassword) {
            try
            {
                //
                // This block obtains the person metadata ID that is needed to change the password
                //
                // Defines the GetIdentityInfo 'ReturnUnrestrictedSource' option.
                final String[][] options ={{"ReturnUnrestrictedSource",""}};
                // Defines a stringholder for the info output parameter.
                VariableArray2dOfStringHolder info = new VariableArray2dOfStringHolder();
                // Issues the GetInfo method for the provided iSecurity connection user.
                iSecurity.GetInfo("GetIdentityInfo","Person:"+IdentityName, options, info);
                String[][] returnArray = info.value;
                String personMetaID = new String();
                for (int i=0; i< returnArray.length; i++ )
                {
                    System.out.println(returnArray[i][0] + "=" + returnArray[i][1]);
                    if (returnArray[i][0].compareTo("IdentityObjectID") == 0) {
                        personMetaID = returnArray[i][1];
                    }
                }
                objectStore = _factory.createObjectStore();
                person = (Person) _factory.createComplexMetadataObject(objectStore, IdentityName, MetadataObjects.PERSON, personMetaID);
                iSecurity.SetInternalPassword(IdentityName, IdentityPassword);
                person.updateMetadataAll();
                System.out.println("Password has been changed.");
                return 0; // success
            }
            catch (MdException e)
            {
                Throwable t = e.getCause();
                if (t != null)
                {
                    String ErrorType = e.getSASMessageSeverity();
                    String ErrorMsg = e.getSASMessage();
                    if (ErrorType == null)
                    {
                        // If there is no SAS server message, write a Java/CORBA message.
                    }
                    else
                    {
                        // If there is a message from the server:
                        System.out.println(ErrorType + ": " + ErrorMsg);
                    }
                    if (t instanceof org.omg.CORBA.COMM_FAILURE)
                    {
                        // If there is an invalid port number or host name:
                        System.out.println(e.getLocalizedMessage());
                    }
                    else if (t instanceof org.omg.CORBA.NO_PERMISSION)
                    {
                        // If there is an invalid user ID or password:
                        System.out.println(e.getLocalizedMessage());
                    }
                }
                else
                {
                    // If we cannot find a nested exception, get message and print.
                    System.out.println(e.getLocalizedMessage());
                }
                // If there is an error, print the entire stack trace.
                e.printStackTrace();
            }
            catch (RemoteException e)
            {
                // Unknown exception.
                e.printStackTrace();
            }
            catch (Exception e)
            {
                // Unknown exception.
                e.printStackTrace();
            }
            System.out.println("Failure: Password has NOT been changed.");
            return 1; // failure
        }
    }
    

    2. Resolve depends

    Pay attention to imports in class. To enable execute the code below necessary set CLASSPATH enironment variable.

    On linux you can add the next command in %SASConfig%/Lev1/level_env_usermods.sh:

    export CLASSPATH=$CLASSPATH:%pathToJar%
    

    On Windows you can add/change environment variable by Advanced system settings


    So where should you search jar files? They are in folder:

    %SASHome%/SASVersionedJarRepository/eclipse/plugins/

    Which files i should include in path?

    I've include all that used in OMI(Open Metadata Interface).Also I've added log4j.jar (not working without this jar. Your promts will be helpful):

    Choose files from nearest release. Example:

    enter image description here

    Here I'm set file from v940m3f (fix release).
    Other ways is here.

    3. Compile setPasswd.jar

    I'm tried use internal javac.exe into SAS, but it's not worked properly. So ou need to download JDK to compile jars. I've create Bat-file:

    "C:\Program Files\Java\jdk1.8.0_121\bin\javac.exe" -source 1.7  -target 1.7 setPasswd.java
    "C:\Program Files\Java\jdk1.8.0_121\bin\jar" -cf setPasswd.jar setPasswd.class
    

    Paramethers -source and -target will helpful if your version of JDK is upper, that usses in SAS. Version of "sas"-java you can see by:

    PROC javainfo all;
    run; 
    

    Search the next string in log:

    java.vm.specification.version = 1.7

    4. Finally. SAS Base call

    Now we can call Java code by this method (All methods available here):

    data test;
          dcl javaobj j ("setPasswd");
          j.callIntMethod("connectToMetadata", "%SERVER%", "%PORT%", "%ADMIN%", "%{SAS002}HASHPASSORPASS%", rc1);
          j.callIntMethod("changePasswd", "testPassLogin", "pass1", rc2);
          j.delete();
    run;
    

    In log:

    UserClass=Normal  
    AuthenticatedUserid=Unknown  
    IdentityName=testPass  
    IdentityType=Person  
    IdentityObjectID=A56RQPC2.AP00000I  
    Password has been changed.  
    

    Now time to test. Create new user with no passwords.

    enter image description here

    Execute code:

    data test;
          dcl javaobj j ("setPasswd");
          j.callIntMethod("connectToMetadata", "&server.", "&port.", "&adm", "&pass", rc1);
          j.callIntMethod("changePasswd", "TestUserForStack", "Overflow", rc2);
          j.delete();
    run;
    

    Now our user has InternalLogin object.

    enter image description here

    Thanx.