SAS-Programm zur Ansteuerung eines Webservices zum Setzen von Passwörtern in Microsoft Office-Dateien.

22. Januar 2024

Ausgangslage

Im Rahmen der Deaktivierung der Möglichkeit Systembefehle über X-Commands aus SAS auszulösen stellte sich die Frage „Wie können Passwörter für in SAS erstellte Microsoft Office-Dateien (xlsx, docx oder pptx) direkt über SAS gesetzt werden?“. Dies wurde früher über einen Java Aufruf in Kombination mit dem X-Befehl im SAS-Code gelöst.

Die rettende Idee war, die Office-Datei über SAS an einen Webservice zu geben und diese mit dem gesetzten Passwort wieder abzuholen. Die Datei wird dann mit SAS in das gewünschte Verzeichnis geschrieben und die Antwort des Webservers mit einem Datastep ausgelesen und ins Log geschrieben.

Parameter

Das Makro enthält drei Parameter:

%macro EncryptOffice (officefile,outfile,password) /Minoperator;

  • Officefile gibt den Pfad inklusive des Dateinamens an.

Beispiel:  …\usr$\X123\test.xlsx

  • outfile gibt den Ausgabepfad inklusive des Ausgabedateinamens an.

Beispiel:  …\usr$\X123\test_pw.xlsx

  • Password setzt das gewünschte Passwort

Beispiel: test123

Der erste Teil des Makros fängt bestimmte Fehler ab, die bei der Eingabe der Parameter vorkommen.

Z.B. wird auch geprüft welche Endung die Input- und Outputdateien haben.

%if !&officefile! eq !! %then
%do;
%put ERROR: Übergabeparameter fehlt! Eingabe: %nrbquote(%)EncryptOffice (Office-Datei inkl. Pfad, Ausgabedatei inkl. Pfad, Verschlüsselungskennwort);
%return;
%end;

%else %if !&outfile! eq !! %then
%do;
%put ERROR: Übergabeparameter fehlt! Eingabe: %nrbquote(%)EncryptOffice (Office-Datei inkl. Pfad, Ausgabedatei inkl. Pfad, Verschlüsselungskennwort);
%return;
%end;

%if !&password! eq !! %then
%do;
%put ERROR: Übergabeparameter fehlt! Eingabe: %nrbquote(%)EncryptOffice (Office-Datei inkl. Pfad, Ausgabedatei inkl. Pfad, Verschlüsselungskennwort);
%return;
%end;

DATA _NULL_;
	officefile = "&officefile";
	EndungInfile = UPCASE(SUBSTR(officefile,FIND(officefile,".",12)+1));
	PUT EndungInfile=;
	outfile = "&outfile";
	EndungOutfile = UPCASE(SUBSTR(outfile,FIND(outfile,".",12)+1));
	PUT EndungOutfile=;

	IF EndungInfile NOT IN ("XLSX" "DOCX" "PPTX") THEN
		DO;
			PUT "ERROR: Endung der Eingabedatei ist nicht xlsx, docx, oder pptx";
		END;

	IF EndungOutfile NOT IN ("XLSX" "DOCX" "PPTX") THEN
		DO;
			PUT "ERROR: Endung der Ausgabedatei ist nicht xlsx, docx, oder pptx";
		END;
RUN;

Übergabe der Datei an Webservice.

Herzstück der SAS-Lösung ist der PROC http, der die Datei an den Webserver (in diesem Fall ein Tomcat mit einem Webservice der das Passwort setzt) schickt.

Vorher werden die URLs für die SAS-Umgebungen (ermittelt anhand einer Systemvariablen SAS_ENV) in die Makrovariable URL geschrieben.

%if %sysget(SAS_ENV) = ENTWICKLUNG %then
%let URL = https://ABCe.XYZ.de:8080/OfficeEncrypt;

%else %if %sysget(SAS_ENV) = TEST %then
%let URL = https://ABCt.XYZ.de:8080/OfficeEncrypt;

%else %if %sysget(SAS_ENV) = Abnahme %then
%let URL = https://ABCa.XYZ.de:8080/OfficeEncrypt;

%else %if %sysget(SAS_ENV) = PRODUKTION %then
%let URL = https://ABCp.XYZ.de:8080/OfficeEncrypt;

%ELSE %PUT ERROR: SAS-Umgebung kann nicht ermittelt werden. Bitte wenden Sie sich an den User Help Desk.;
%put Parameter: %sysget(SAS_ENV) &=URL;

Filename Statements für Input- und Ouputfiles

Die aus den Parametern stammenden Dateien inklusive Pfad werden über das Filename Statement mit den Filerefs input und output verknüpft.

filename input "&officefile";
filename output "&outfile";

Input wird an den Webserver geschickt und die mit Passwort versehene Officedatei über ouput wieder abgeholt und im gewünschten Verzeichnis gespeichert.

Die Option (nach dem in= Statement) MULTI erlaubt den Upload von Dateien inklusive mehrerer Parametern nach der Option FORM. 

PROC HTTP

proc http
	url="&URL"
	in = MULTI FORM ( "password" = "&password" ,
	"file" = input )
	out=output
;
run;

Bei Erfolg wird die Datei über output im angegebenen Pfad mit dem gewünschten Namen und gesetztem Passwort gespeichert.

Fehlerhandling Webservice

Falls ein Fehler vom Webserver gemeldet wird, wird dieser ins Log ausgegeben.

Hierzu wird geprüft, ob die automatische PROC HTTP Variable &SYS_PROCHTTP_STATUS_CODE einen Wert größer gleich 400 zurückgibt. Ist dies der Fall kann der konkrete Fehler aus dem Filename output über einen Datastep ausgelesen werden und ins Log geschrieben werden. Diese Fehlermeldung kommt direkt vom Webserver. Die Makrovariablen &SYS_PROCHTTP_STATUS_CODE und &SYS_PROCHTTP_STATUS_PHRASE werden von PROC HTTP gesetzt.

%IF &SYS_PROCHTTP_STATUS_CODE >= 400 %THEN
		%DO;

			DATA _NULL_;
				LENGTH MSG $200;
				INFILE output DSD MISSOVER DELIMITER="*";
				INPUT MSG $;
				PUT MSG=;
			RUN;

			%PUT ERROR: Das Verschlüsseln der Office-Datei war nicht erfolgreich!;
			%PUT ERROR: &=SYS_PROCHTTP_STATUS_CODE;
			%PUT ERROR: &=SYS_PROCHTTP_STATUS_PHRASE;

			%RETURN;
		%END;

Kompletter Makrocode:

%macro EncryptOffice (officefile,outfile,password) /Minoperator;
	%if !&officefile! eq !! %then
		%do;
			%put ERROR: Übergabeparameter fehlt! Eingabe: %nrbquote(%)EncryptOffice (Office-Datei inkl. Pfad, Ausgabedatei inkl. Pfad, Verschlüsselungskennwort);

			%return;
		%end;
	%else %if !&outfile! eq !! %then
		%do;
			%put ERROR: Übergabeparameter fehlt! Eingabe: %nrbquote(%)EncryptOffice (Office-Datei inkl. Pfad, Ausgabedatei inkl. Pfad, Verschlüsselungskennwort);

			%return;
		%end;

	%if !&password! eq !! %then
		%do;
			%put ERROR: Übergabeparameter fehlt! Eingabe: %nrbquote(%)EncryptOffice (Office-Datei inkl. Pfad, Ausgabedatei inkl. Pfad, Verschlüsselungskennwort);

			%return;
		%end;

DATA _NULL_;
	officefile = "&officefile";
	EndungInfile = UPCASE(SUBSTR(officefile,FIND(officefile,".",12)+1));
	PUT EndungInfile=;
	outfile = "&outfile";
	EndungOutfile = UPCASE(SUBSTR(outfile,FIND(outfile,".",12)+1));
	PUT EndungOutfile=;

	IF EndungInfile NOT IN ("XLSX" "DOCX" "PPTX") THEN
		DO;
			PUT "ERROR: Endung der Eingabedatei ist nicht xlsx, docx, oder pptx";
		END;

	IF EndungOutfile NOT IN ("XLSX" "DOCX" "PPTX") THEN
		DO;
			PUT "ERROR: Endung der Ausgabedatei ist nicht xlsx, docx, oder pptx";
		END;
	RUN;

%if %sysget(SAS_ENV) = ENTWICKLUNG %then
%let URL = https://ABCe.XYZ.de:8080/OfficeEncrypt;

%else %if %sysget(SAS_ENV) = TEST %then
%let URL = https://ABCt.XYZ.de:8080/OfficeEncrypt;

%else %if %sysget(SAS_ENV) = Abnahme %then
%let URL = https://ABCa.XYZ.de:8080/OfficeEncrypt;

%else %if %sysget(SAS_ENV) = PRODUKTION %then
%let URL = https://ABCp.XYZ.de:8080/OfficeEncrypt;

%ELSE %PUT ERROR: SAS-Umgebung kann nicht ermittelt werden. Bitte wenden Sie sich an den User Help Desk.;
%put Parameter: %sysget(SAS_ENV) &=URL;
	
	filename input "&officefile";
	filename output "&outfile";

	proc http
		url="&URL"
		in = MULTI FORM ( "password" = "&password" ,
		"file" = input )
		out=output
	;
	run;

	%IF &SYS_PROCHTTP_STATUS_CODE >= 400 %THEN
		%DO;

			DATA _NULL_;
				LENGTH MSG $200;
				INFILE output DSD MISSOVER DELIMITER="*";
				INPUT MSG $;
				PUT MSG=;
			RUN;

			%PUT ERROR: Das Verschlüsseln der Office-Datei war nicht erfolgreich!;
			%PUT ERROR: &=SYS_PROCHTTP_STATUS_CODE;
			%PUT ERROR: &=SYS_PROCHTTP_STATUS_PHRASE;

			%RETURN;
		%END;
%mend EncryptOffice;

Beispielaufruf

%EncryptOffice (…\usr$\X123\test.xlsx…\usr$\X123\test_pw.xlsx,test123);

Links und Anmerkungen zum Webservice

Unter „Usage http Procedure“ finden sich Informationen zu den Optionen MULTI und FORM SAS Help Center: Using the FORM and QUERY Options with PROC HTTP

Der Code für das Verschlüsseln entstammt im Wesentlichen dem Tool Apache POI und kann unter https://poi.apache.org/encryption.html "XML-based formats - Encryption" gefunden werden. Es müssen natürlich dann auch dann die entsprechenden Apace POI Bibliotheken und abhängigen Libraries eingebunden werden. Des Weiteren muss um den obig genannten Beispielcode ein entsprechendes HTTPServlet außen herum gebaut werden . Es empfiehlt sich zudem, direkt die vom HTTPServlet bereitgestellten HTTP-Streams (Input und Output) zu nutzen anstatt wie im Beispiel (temporäre) Dateien im Filesystem.

Beim Parameterauslesen gibt eine kleine Besonderheit aufgrund des HTTP-Multipart-Formats. Beim Headerauslesen ist zusätzlicher Code nötig, um auf den Filename zuzugreifen. Informationen hierzu findet man unter: https://docs.oracle.com/javaee/6/tutorial/doc/glraq.html

(Funktion getFileName())

Das Servlet wurde über folgenden Eintrag in der web.xml bekannt gemacht.

<servlet>
    <servlet-name>OfficeEncryptServlet</servlet-name>
    <servlet-class>de.abc.OfficeEncryptServlet</servlet-class>
    <multipart-config>
    	<max-file-size> 20848820 </max-file-size>
    	<max-request-size> 418018841 </max-request-size>
    	<file-size-threshold> 1048576 </file-size-threshold>
    </multipart-config>
  </servlet>

Informationen hierzu finden sich unter:

https://docs.oracle.com/javaee/6/tutorial/doc/gmhal.html

Dank

Mein besonderer Dank geht an Bernhard Gruber für die Umsetzung und die Anmerkungen zum genutzten Webservice.

cross-circleCookie Consent Banner von Real Cookie Banner