sassoftware / saspy

A Python interface module to the SAS System. It works with Linux, Windows, and Mainframe SAS as well as with SAS in Viya.
https://sassoftware.github.io/saspy
Other
367 stars 149 forks source link

How to implement organisation-specific configuration for multiple users #316

Closed tomsutch closed 3 years ago

tomsutch commented 3 years ago

Hello

I've been using saspy for a while and am enjoying the experience, thanks! I'd like to publicise among other SAS users in my organisation amenable to Python (or vice versa!), and make it as easy for them to use as possible by providing them with pre-configured access methods.

So we've got three SAS servers running on Windows - production, dev and test - and two possible ways of accessing them via saspy: IOM (& Java) and COM.

What I'd like is for SAS users to be able to call something like

sas = MYORG_SASsession(server = 'prd', method = 'COM', ...)

and for it to default to the production server, COM (for example), and pick up omruser from the username, and look up the necessary values to pass to saspy. I could then package this wrapper up so it could be installed easily using pip from an internal version control system.

To achieve this, my idea was to write a new class MYORGSASsession() as a subclass of saspy.SASsession(), translate the org-specific stuff into the arguments that saspy needs (iomhost and so on), then call super.__init__. But it looks like to do that I would also have to subclass a SASconfig() object, because as it's written the __init__ method deals with handling the config file locations, and profiles within the chosen file, which I don't want. (All I want to be able to do is instantiate a config object with the specific arguments needed for the connection - eg iomhost, iomuser, omruser, omrpw, encoding, java, provider, classid.) And that in turn would mean I would have to reimplement the __init__ method. That's quite a lot of code to copy and maintain...

An alternative I thought of was having a small local package that could install a sascfg_personal.py file somewhere where saspy could pick it up. Then I could provide the various possibilities as profiles within this. Presumably to do this I would have to have an initialisation function in the package to copy the sascfg_personal.py file to ~/.config/saspy?

Do either of these sound like good ways, or is there a better way that I can do this? I'm not much of a Python developer so I may be missing something obvious!

tomweber-sas commented 3 years ago

Hey @tomsutch, I can help you figure out the best way to go about hits. And glad you're enjoying it :)

So, lets start with the existing functionality and capabilities, and then see what else is needed. I assume your SAS servers are Workspace servers, and if so then yes, you can use either access method, IOM (from any client) or COM (from a windows client). I would suggest IOM, as I wrote and support that completely, along with the STDIO (over SSH or not) and HTTP access methods. The COM access method is a user contributed access methods, which is pretty cool, though I can't 100% always keep it up with every fix and enhancement I can do to the other 3. The current version of saspy has the jar files required by java, included, and is much easier to configure than it was originally, before I was allowed to have those jar files in the repo (no more classpath required in the config file!).

So, the sascfg_personal.py config file can have configuration definitions for each of your workspace servers; dev, test prod. You can name them whatever you want, so it's obvious which one you're trying to access. These config definitions should be able to be standard for any user. Since you no longer need client specific paths to jar files, that's not longer an issue with having to have client specific paths in the config. You do need Java installed on the client, though if your clients are windows, and the path to java is in the system path (usually set up when you install java on windows), you can simply specify 'java' instead of a fully qualified path ('C:\where\ever\java\is\installed\java.exe').

As for credentials, you can use the _authinfo file and the authkey= config parameter so the users credentials (metadata user/pw) are retrieved automatically by saspy. This eliminates the need for being prompted to enter your user/pw when you make a connection. https://sassoftware.github.io/saspy/install.html#the-authinfo-file-authinfo-on-windows

So, if we start with this, we would have a sascfg_personal.py and the following instructions/requirements:

sascfg_personal.py (save this in your home directory: $HOME.config\saspy\sascfg_personal.py )

# in case you need to specify the fully qualified path to java, change 'java' to the full path
# java_path = 'c:\\path\\to\\java.exe'
# java_path = '/usr/bin/java'
java_path = 'java'

SAS_config_names = ['dev', test', 'prod']

dev         = {'java'   : java_path,
            'iomhost'   : 'dev.object.spawner.machine',
            'iomport'   : 8591,
            'iauthkey'  : 'saspy_dev',
            }

test        = {'java'   : java_path,
            'iomhost'   : 'test.object.spawner.machine',
            'iomport'   : 8591,
            'iauthkey'  : 'saspy_test',
            }

prod        = {'java'   : java_path,
            'iomhost'   : 'prod.object.spawner.machine',
            'iomport'   : 8591,
            'iauthkey'  : 'saspy_prod',
            }

_authinfo file (save this in your home directory: $HOME_authinfo )

saspy_dev   user   user_id_for_dev   password  pw_for_dev_system
saspy_test  user   user_id_for_test  password  pw_for_test_system
saspy_prod  user   user_id_for_prod  password  pw_for_prod_system

Then you have the following requirements:

1) install java, if you don't already have it installed (I suggest v 1.8 or newer), be sure 'java' is found in the system path 2) copy the 2 files above to the locations specified, and replace your metadata credentials in the _authinfo file

When you use saspy now, you just say which system you are trying to connect to. If you just submit saspy.SASsession() then it will prompt you for which to connect to.

import saspy
prod = saspy.SASsession(cfgname='prod')
prod

dev  = saspy.SASsession(cfgname='dev')
dev

sas = saspy.SASsession()
Please enter the name of the SAS Config you wish to run. Available Configs are: ['dev', test', 'prod']

So, given this as a starting point, what do you think? What else do you need. Are all the users windows clients, or is anyone running on linux or other? Is so, they would need to specify the full path to java ('/usr/bin/java' or whatever), and the authinfo file is then (dot) .authinfo not (underscore) _authinfo, like on windows, but that's just simple things to adjust.

What do you think?

tomsutch commented 3 years ago

Thanks for your helpful reply Tom, useful to have a template. Yes, all users would be connecting from Windows clients.

I'd already had a go at writing a common sascfg_personal.py like that, but was seeking to have a way to avoid users installing files manually and maintaining passwords. Having thought about things a bit more today, I think the simplest thing for us to do is try to get Integrated Windows Authentication set up (as usernames/passwords are just standard Windows ones) - that would then make saspy configuration a bit easier for users as no _authinfo file would be needed. It would also make for a smoother experience in the SAS desktop apps all round! I'll talk to our IT people :)

I haven't yet given up on the idea of having a local wrapper package to configure and extend saspy, as I was thinking about writing Python interfaces to some of our autocall SAS macros that carry out common tasks using our data.

tomweber-sas commented 3 years ago

Cool, yes, IWA would eliminate the need for the _authinfo file, and you would replace the authkey with sspi in the config definitions (look like I miss typed authkey as iauthkey above):

dev         = {'java'   : java_path,
            'iomhost'   : 'dev.object.spawner.machine',
            'iomport'   : 8591,
            'authkey'  : 'saspy_dev',
            }
becomes
dev         = {'java'   : java_path,
            'iomhost'   : 'dev.object.spawner.machine',
            'iomport'   : 8591,
            'sspi'      : True',
            }

But, with using IWA, you add the requirement to have the sspiauth.dll file on the client, and in the system Path to be found. That may be less appealing, though again, all they need to do is copy that file to a common directory and add it to their path, or just add the following to the config so they don't have to add that dir to their path:

import os
os.environ["PATH"] += ";C:\\Dir\\where\\sspiauth.dll\is"

Tradeoffs, tradeoffs. Sorry about that. But, if they are capable of installing python and using this to do work, I bet they can handle copying a file or two to a specific place, and then they are done. It's a one time thing either way. So, you have a couple of possible ways to do this. If you go IWA, you just need to make the config file changes above, and it will be the same file for everyone, and then you just need for them to be able to copy the sspiauth.dll from somewhere, if they don't have it already, to whatever dir you put in the config (adding it to the path dynamically).

Hope that helps, Tom

tomsutch commented 3 years ago

Thanks for all your help Tom, this is really useful. I'll close the issue now as I've got more than enough to get going with :)

tomweber-sas commented 3 years ago

Hey, that's great. Just let me know if you need anything else! Tom