Browse Source

fixed crlf issue

master
Gerry Nelson 7 years ago
parent
commit
23a6d48e92
  1. 220
      ContributorAgreement.txt
  2. 118
      INSTALL.md
  3. 568
      README.md
  4. 184
      createfolders.py
  5. 586
      explainaccess.py
  6. 248
      listrules.py
  7. 116
      loginviauthinfo.py
  8. 130
      showsetup.py
  9. 278
      unittestsadm33.sh
  10. 362
      unittestsadm34.sh
  11. 216
      updatedomain.py
  12. 196
      updatepreferences.py

220
ContributorAgreement.txt

@ -1,111 +1,111 @@
Contributor Agreement
Version 1.1
Contributions to this software are accepted only when they are
properly accompanied by a Contributor Agreement. The Contributor
Agreement for this software is the Developer's Certificate of Origin
1.1 (DCO) as provided with and required for accepting contributions
to the Linux kernel.
In each contribution proposed to be included in this software, the
developer must include a "sign-off" that denotes consent to the
terms of the Developer's Certificate of Origin. The sign-off is
a line of text in the description that accompanies the change,
certifying that you have the right to provide the contribution
to be included. For changes provided in source code control (for
example, via a Git pull request) the sign-off must be included in
the commit message in source code control. For changes provided
in email or issue tracking, the sign-off must be included in the
email or the issue, and the sign-off will be incorporated into the
permanent commit message if the contribution is accepted into the
official source code.
If you can certify the below:
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
then you just add a line saying
Signed-off-by: Random J Developer <random@developer.example.org>
Contributor Agreement
Version 1.1
Contributions to this software are accepted only when they are
properly accompanied by a Contributor Agreement. The Contributor
Agreement for this software is the Developer's Certificate of Origin
1.1 (DCO) as provided with and required for accepting contributions
to the Linux kernel.
In each contribution proposed to be included in this software, the
developer must include a "sign-off" that denotes consent to the
terms of the Developer's Certificate of Origin. The sign-off is
a line of text in the description that accompanies the change,
certifying that you have the right to provide the contribution
to be included. For changes provided in source code control (for
example, via a Git pull request) the sign-off must be included in
the commit message in source code control. For changes provided
in email or issue tracking, the sign-off must be included in the
email or the issue, and the sign-off will be incorporated into the
permanent commit message if the contribution is accepted into the
official source code.
If you can certify the below:
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
then you just add a line saying
Signed-off-by: Random J Developer <random@developer.example.org>
using your real name (sorry, no pseudonyms or anonymous contributions.)

118
INSTALL.md

@ -1,59 +1,59 @@
**pyviyatools Install Intructions**
The pyviyatools are a set of command-line tools that call the SAS Viya REST API's from python. The tools can be used to make direct calls to any rest-endpoint (like a CURL command) or to build additional tools that make multiple rest calls to provide more complex functionality.
**INSTALL**
The tools are a package of files and should be downloaded as such. The individual files are not useable without the package.
The tools should be installed on the same machine that hosts the Viya command-line interfaces(CLI). The following command will install a copy of the tools in a sub-directory(pyviyatools) of the current directory.
*git clone https://github.com/sassoftware/pyviyatools.git*
**Authenticate**
The pyviya tools use the sas-admin auth CLI to authenticate to Viya. To use the tool you must create a profile and authenticate. This process is documented in the SAS Viya Administration guide here.
http://documentation.sas.com/?cdcId=calcdc&cdcVersion=3.3&docsetId=calcli&docsetTarget=n1e2dehluji7jon1gk69yggc6i28.htm&locale=en
If your environment is enabled for Transport Layer Security (TLS), you must set the SSL_CERT_FILE environment variable to the path location of the trustedcerts.pem file (if using the SAS default truststore)
or the path location of your site-signed certificate (if using an internal truststore).
In addition you may need to set REQUESTS_CA_BUNDLE to the certificate location so that the requests library can find the certificates.
The tool will automatically use the default profile.
1. To create a profile as a SAS Administrator logon to the machine that contains the Viya CLI's
2. Run */opt/sas/viya/home/bin/sas-admin profile init* and when prompter enter the base endpoint of your Viya server for example: http://myviyaserver.blah.com. You may enter your personal preference at the other prompts.
3. After you create the profile there are two options to authenticate
* Run */opt/sas/viya/home/bin/sas-admin auth login* to authenticate to Viya enter the userid and password of the SAS Administrator when prompted.
* Create an .authinfo file in your home directory with your userid and password and use **loginviauthinfo.py** to authenticate with the credentials in the file (you can use different authinfo files with the -f option)
The CLI allows for multiple profiles. To use a profile other than the default profile, for example newprofile
1. Create a named profile *sas-admin --profile newprofile profile init*
2. Logon with the named profile *sas-admin --profile newprofile auth login*
3. Set the SAS_CLI_PROFILE environment variable to the name of the profile
* LINUX: *export SAS_CLI_PROFILE=newprofile*
* WINDOWS: *set SAS_CLI_PROFILE=newprofile*
4. To revert to using the default variable reset the environment variable
* LINUX: *unset SAS_CLI_PROFILE*
* WINDOWS: *set SAS_CLI_PROFILE=*
The tools are self-documenting, for help on any tool call the tool passing -h or --help.
**TEST**
1. Execute a test call. Change directory to your install directory and run:
*callrestapi.py -e /folders/folders -m get*
The call should return the JSON for the folders in the viya deployment. Be patient it might take a few seconds the first time.
2. To see the current setup, including python and package versions, environment variable settings, profile information and current user. Run
*showsetup.py*
**pyviyatools Install Intructions**
The pyviyatools are a set of command-line tools that call the SAS Viya REST API's from python. The tools can be used to make direct calls to any rest-endpoint (like a CURL command) or to build additional tools that make multiple rest calls to provide more complex functionality.
**INSTALL**
The tools are a package of files and should be downloaded as such. The individual files are not useable without the package.
The tools should be installed on the same machine that hosts the Viya command-line interfaces(CLI). The following command will install a copy of the tools in a sub-directory(pyviyatools) of the current directory.
*git clone https://github.com/sassoftware/pyviyatools.git*
**Authenticate**
The pyviya tools use the sas-admin auth CLI to authenticate to Viya. To use the tool you must create a profile and authenticate. This process is documented in the SAS Viya Administration guide here.
http://documentation.sas.com/?cdcId=calcdc&cdcVersion=3.3&docsetId=calcli&docsetTarget=n1e2dehluji7jon1gk69yggc6i28.htm&locale=en
If your environment is enabled for Transport Layer Security (TLS), you must set the SSL_CERT_FILE environment variable to the path location of the trustedcerts.pem file (if using the SAS default truststore)
or the path location of your site-signed certificate (if using an internal truststore).
In addition you may need to set REQUESTS_CA_BUNDLE to the certificate location so that the requests library can find the certificates.
The tool will automatically use the default profile.
1. To create a profile as a SAS Administrator logon to the machine that contains the Viya CLI's
2. Run */opt/sas/viya/home/bin/sas-admin profile init* and when prompter enter the base endpoint of your Viya server for example: http://myviyaserver.blah.com. You may enter your personal preference at the other prompts.
3. After you create the profile there are two options to authenticate
* Run */opt/sas/viya/home/bin/sas-admin auth login* to authenticate to Viya enter the userid and password of the SAS Administrator when prompted.
* Create an .authinfo file in your home directory with your userid and password and use **loginviauthinfo.py** to authenticate with the credentials in the file (you can use different authinfo files with the -f option)
The CLI allows for multiple profiles. To use a profile other than the default profile, for example newprofile
1. Create a named profile *sas-admin --profile newprofile profile init*
2. Logon with the named profile *sas-admin --profile newprofile auth login*
3. Set the SAS_CLI_PROFILE environment variable to the name of the profile
* LINUX: *export SAS_CLI_PROFILE=newprofile*
* WINDOWS: *set SAS_CLI_PROFILE=newprofile*
4. To revert to using the default variable reset the environment variable
* LINUX: *unset SAS_CLI_PROFILE*
* WINDOWS: *set SAS_CLI_PROFILE=*
The tools are self-documenting, for help on any tool call the tool passing -h or --help.
**TEST**
1. Execute a test call. Change directory to your install directory and run:
*callrestapi.py -e /folders/folders -m get*
The call should return the JSON for the folders in the viya deployment. Be patient it might take a few seconds the first time.
2. To see the current setup, including python and package versions, environment variable settings, profile information and current user. Run
*showsetup.py*

568
README.md

@ -1,284 +1,284 @@
# Python Tools for SAS Viya
The pyviyatools are a set of command-line tools that call the SAS Viya REST API's from python. The tools can be used to make direct calls to any rest-endpoint (like a CURL command) or to build additional tools that make multiple rest calls to provide more complex functionality. The tools are designed to be used in conjunction with the sas-admin command line interfaces(CLI).
## Getting Started
### Documentation
You can find the SAS Open API's documented: https://developer.sas.com/apis/rest/
Some other useful links
* https://developer.sas.com/apis/rest/#filters
* https://developer.sas.com/apis/rest/#pagination
* https://developer.sas.com/apis/rest/#sorting
### Prerequisites
The tools requires either python 2 or python 3.
The following python libraries are used:
* requests http://docs.python-requests.org/en/master/
* sys
* json
* os
* netrc
* subprocess
* platform
* argeparse
### Installing
Please use the installation intructions in the file INSTALL.md [INSTALL.md](INSTALL.md)
### Running
The pyviya tools use the sas-admin auth CLI to authenticate to Viya. To use the tool you must create a profile and authenticate.
This process is documented in the SAS Viya Administration guide here.
http://documentation.sas.com/?cdcId=calcdc&cdcVersion=3.3&docsetId=calcli&docsetTarget=n1e2dehluji7jon1gk69yggc6i28.htm&locale=en
#### Creating a Profile and Logging on
The tool will automatically use the default profile.
1. To create a profile as a SAS Administrator logon to the machine that contains the Viya CLI's
2. Run */opt/sas/viya/home/bin/sas-admin profile init* and when prompter enter the base endpoint of your Viya server for example: http://myviyaserver.blah.com. You may enter your personal preference at the other prompts.
3. After you create the profile there are two options to authenticate
* Run */opt/sas/viya/home/bin/sas-admin auth login* to authenticate to Viya enter the userid and password of the SAS Administrator when prompted.
* Create an .authinfo file in your home directory with your userid and password and use **loginviauthinfo.py** to authenticate with the credentials in the file (you can use different authinfo files with the -f option)
The CLI allows for multiple profiles. To use a profile other than the default profile, for example newprofile
1. Create a named profile *sas-admin --profile newprofile profile init*
2. Logon with the named profile *sas-admin --profile newprofile auth login*
3. Set the SAS_CLI_PROFILE environment variable to the name of the profile
* LINUX: *export SAS_CLI_PROFILE=newprofile*
* WINDOWS: *set SAS_CLI_PROFILE=newprofile*
4. When the SAS_CLI_PROFILE is set the tool will use the profile stored in the variable
5. To revert to using the default variable reset the environment variable
* LINUX: *unset SAS_CLI_PROFILE*
* WINDOWS: *set SAS_CLI_PROFILE=*
The tools are self-documenting, for help on any tool call the tool passing -h or --help.
python *\<pathtotool\>\<toolname.py\>* -h
**Available Tools**
**callrestapi**
callrestapi is a general tool, and the building block for all the other tools.
callrestapi will call the Viya REST API and return json or optionally a simplified output. It can call any viya REST method. (Like a curl command).
*usage: callrestapi.py [-h] -e ENDPOINT -m {get,put,post,delete} [-i INPUTFILE] [-a ACCEPTTYPE] [-c CONTENTTYPE] [-o {csv,json,simple}]*
You must pass a method and endpoint. You can optionally pass json, content type headers or the -t flag to change output from json to basic text.
**List of some of the Additional Tools Available**
Additional tools provide more complex functionality by combining multiple calls to the callrestapi function, and post-processing the outpuit that is returned.
* **getfolderid** returns the id of the folder based on the full folder path
* **deletefolder** deletes a folder based on the full folder path
* **deletefolderandcontent** deletes a folder and any reports that are stored in the folder
* **movecontent** moves the content from a source to a target folder
* **getconfigurationproperties** lists the name/value pairs of a configuration
* **testfolderaccess** tests if a user or group has access to a folder
* **createbinarybackup.py** creates a binary backup job
* **createdomain.py** creates an authentication domain
* **listrules.py** list authorization rules subset on a principal and/or a uri
* **loginviauthinfo.py** use an authinfo file to authenticate to the CLI
* **updateprefences.py** update preferences for a user or group of users
* **updatedomain.py** Load a set of userids and passwords to a Viya domain from a csv file
* **createfolders.py** Create a set of Viya folders from a csv file
* **explainaccess.py** Explains access for a folder, object or service endpoint
Check back for additional tools and if you build a tool feel free to contribute it to the collection.
## Examples
The example calls assume you are in the directory where you installed the tools. If you are not you can prepemd the directory to the filename, for example /opt/pyviyatools/callrestapi.py
If you are using the tool on windows the syntax is:
*python toolname.py*
The following examples are Linux.
\# Login to Viya using default authinfo file ~/.authinfo
*./loginviauthinfo.py*
\# Login to Viya using an authinfo file
*./loginviauthinfo.py -f ~/.authinfo_geladm*
\#return all the rest calls that can be made to the folders endpoint
*./callrestapi.py -e /folders -m get*
\#return the json for all the folders/folders
*./callrestapi.py -e /folders/folders -m get*
\#return simple text for all the folders/folders
*./callrestapi.py -e /folders/folders -m get -o simple*
\#rest calls often limit the results returned the text output will tell you returned and total available items
\#in this call set a limit above the total items to see everything
*./callrestapi.py -e /folders/folders?limit=500 -m get -o simple*
\#return the json for all the identities
*./callrestapi.py -e /identities/identities -m get*
\#return the json for all the identities output to a file
*./callrestapi.py -e /identities/identities -m get > identities.json*
\#refresh the identities cache
*./callrestapi.py -e /identities/userCount -m get*
*./callrestapi.py -e /identities/cache/refreshes -m post*
\# pass the folder path and return the folder id and uri
*./getfolderid.py -f /gelcontent*
\# delete a folder based on its path
*./deletefolder.py -f /gelcontent*
\#return a set of configuration properties
*./getconfigurationproperties.py -c sas.identities.providers.ldap.user -o simple*
\#create a domain using createdomain
*./createdomain.py -t password -d test -u sasadm -p lnxsas -g "SASAdministrators,HR ,Sales"*
\#Update an existing domain to add credentials from a csv file
*./updatedomain.py -d LASRAuth -f /tmp/myusers.csv*
csv file format
no header row
column1 is userid
column2 is password
column3 is identity
column4 is identity type (user or group)
For example:
myuserid,mypass,Sales,group
acct1,pw1,sasadm,user
\#create a domain using callrestapi. Last part of endpoint is domain name
*./callrestapi.py -e /credentials/domains/\<newdomain\> -m post -i domain.json*
INPUT JSON must be formatted as the endpoint expects. Example:
{
"id": "\<newdomain\>",
"type": "password"
}
\#test folder access
*./root/admin/pyviyatools/testFolderAccess.py -f '/gelcontent' -p Sales -m read -s grant -q*
\#return basic content using accept response type
*./callrestapi.py -m get -e /identities/users/sasadm -a "application/vnd.sas.identity.basic+json"*
\# Display all sasadministrator rules
*./listrules.py --p SASadministrators -o simple*
\# Display all rules that contain SASVisual in the URI
*./listrules.py -u SASVisual -o simple*
\# Create folders from a CSV file
*./createfolders.py" -f /tmp/newfolders.csv*
FORMAT OF CSV file folder path (parents must exist), description
/RnD, Folder under root for R&D
/RnD/reports, reports
/RnD/analysis, analysis
/RnD/data plans, data plans
/temp,My temporary folder
/temp/mystuff, sub-folder
\# Update the theme for the user sasadm
*./updatepreferences.py -t user -tn sasadm -pi OpenUI.Theme.Default -pv sas_hcb*
\#Explain direct and indirect permissions on the folder /folderA/folderB, no header row. For folders, conveyed permissions are shown by default.
*./explainaccess.py -f /folderA/folderB*
\#As above but for a specific user named Heather
*./explainaccess.py -f /folderA/folderB -n Heather -t user*
\#As above with a header row
*./explainaccess.py -f /folderA/folderB --header*
\#As above with a header row and the folder path, which is useful if you concatenate sets of results in one file
*./explainaccess.py -f /folderA/folderB -p --header*
\#As above showing only rows which include a direct grant or prohibit
*./explainaccess.py -f /folderA/folderB --direct_only*
\#Explain direct and indirect permissions on a service endpoint. Note in the results that there are no conveyed permissions. By default they are not shown for URIs.
*./explainaccess.py -u /SASEnvironmentManager/dashboard*
\#As above but including a header row and the create permission, which is relevant for services but not for folders and other objects
*./explainaccess.py -u /SASEnvironmentManager/dashboard --header -l read update delete secure add remove create*
\#Explain direct and indirect permissions on a report, reducing the permissions reported to just read, update, delete and secure, since none of add, remove or create are applicable to a report.
*./explainaccess.py -u /reports/reports/*folder_id *--header -l read update delete secure*
\#Explain direct and indirect permissions on a folder expressed as a URI. Keep the default permissions list, but for completeness we must also specify -c true to request conveyed permissions be displayed, as they are not displayed by default for URIs.
*./explainaccess.py -u /folders/folders/*folder_id *--header -p -c true*
**Troubleshooting**
The most common problem is an expired access token. You may see a message like:
{"errorCode":0,"message":"Cannot convert access token to JSON","details":["traceId: 6bca23b2b3a2cfda","path: /folders/folders"],"remediation":null,"links":[ ],"version":2,"httpStatusCode":401}
To fix the problem generate a new access token /opt/sas/viya/home/bin/sas-admin auth login
To see the current setup, including python and package versions, environment variable settings, profile information and current user. Run
*python showsetup.py*
If you get this error : Login failed. Error with security certificate.
Set the environment variable for the SSL certificate file. For example:
export SSL_CERT_FILE=/opt/sas/viya/config/etc/SASSecurityCertificateFramework/cacerts/trustedcerts.pem
If you get this error:
Raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='intviya01.race.sas.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, u'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:579)'),))
Set the environment variable for the SSL certificate file. For example:
export REQUESTS_CA_BUNDLE=/opt/sas/viya/config/etc/SASSecurityCertificateFramework/cacerts/trustedcerts.pem
## Developing with the existing functions
The file sharedfunctions.py contains a set of generic functions that make it easy to build additional tools.
*callrestapi* is the main function, called by many other programs and by the callrestapi program to make the REST calls. It accepts as parameters:
* reqval: the request value which is the rest endpoint for the request
* reqtype: the type of REST request, get,put, post, delete
* acceptType: optinal accept type content header
* contentType: optional content type content header
* data: optionally a python dictionary created from the json for the rest request
* stoponerror: whether the function will stop all further processing if an error occurs (default 0 to not stop)
## Contributing
We welcome your contributions! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to submit contributions to this project.
## License
This project is licensed under the [Apache 2.0 License](LICENSE).
# Python Tools for SAS Viya
The pyviyatools are a set of command-line tools that call the SAS Viya REST API's from python. The tools can be used to make direct calls to any rest-endpoint (like a CURL command) or to build additional tools that make multiple rest calls to provide more complex functionality. The tools are designed to be used in conjunction with the sas-admin command line interfaces(CLI).
## Getting Started
### Documentation
You can find the SAS Open API's documented: https://developer.sas.com/apis/rest/
Some other useful links
* https://developer.sas.com/apis/rest/#filters
* https://developer.sas.com/apis/rest/#pagination
* https://developer.sas.com/apis/rest/#sorting
### Prerequisites
The tools requires either python 2 or python 3.
The following python libraries are used:
* requests http://docs.python-requests.org/en/master/
* sys
* json
* os
* netrc
* subprocess
* platform
* argeparse
### Installing
Please use the installation intructions in the file INSTALL.md [INSTALL.md](INSTALL.md)
### Running
The pyviya tools use the sas-admin auth CLI to authenticate to Viya. To use the tool you must create a profile and authenticate.
This process is documented in the SAS Viya Administration guide here.
http://documentation.sas.com/?cdcId=calcdc&cdcVersion=3.3&docsetId=calcli&docsetTarget=n1e2dehluji7jon1gk69yggc6i28.htm&locale=en
#### Creating a Profile and Logging on
The tool will automatically use the default profile.
1. To create a profile as a SAS Administrator logon to the machine that contains the Viya CLI's
2. Run */opt/sas/viya/home/bin/sas-admin profile init* and when prompter enter the base endpoint of your Viya server for example: http://myviyaserver.blah.com. You may enter your personal preference at the other prompts.
3. After you create the profile there are two options to authenticate
* Run */opt/sas/viya/home/bin/sas-admin auth login* to authenticate to Viya enter the userid and password of the SAS Administrator when prompted.
* Create an .authinfo file in your home directory with your userid and password and use **loginviauthinfo.py** to authenticate with the credentials in the file (you can use different authinfo files with the -f option)
The CLI allows for multiple profiles. To use a profile other than the default profile, for example newprofile
1. Create a named profile *sas-admin --profile newprofile profile init*
2. Logon with the named profile *sas-admin --profile newprofile auth login*
3. Set the SAS_CLI_PROFILE environment variable to the name of the profile
* LINUX: *export SAS_CLI_PROFILE=newprofile*
* WINDOWS: *set SAS_CLI_PROFILE=newprofile*
4. When the SAS_CLI_PROFILE is set the tool will use the profile stored in the variable
5. To revert to using the default variable reset the environment variable
* LINUX: *unset SAS_CLI_PROFILE*
* WINDOWS: *set SAS_CLI_PROFILE=*
The tools are self-documenting, for help on any tool call the tool passing -h or --help.
python *\<pathtotool\>\<toolname.py\>* -h
**Available Tools**
**callrestapi**
callrestapi is a general tool, and the building block for all the other tools.
callrestapi will call the Viya REST API and return json or optionally a simplified output. It can call any viya REST method. (Like a curl command).
*usage: callrestapi.py [-h] -e ENDPOINT -m {get,put,post,delete} [-i INPUTFILE] [-a ACCEPTTYPE] [-c CONTENTTYPE] [-o {csv,json,simple}]*
You must pass a method and endpoint. You can optionally pass json, content type headers or the -t flag to change output from json to basic text.
**List of some of the Additional Tools Available**
Additional tools provide more complex functionality by combining multiple calls to the callrestapi function, and post-processing the outpuit that is returned.
* **getfolderid** returns the id of the folder based on the full folder path
* **deletefolder** deletes a folder based on the full folder path
* **deletefolderandcontent** deletes a folder and any reports that are stored in the folder
* **movecontent** moves the content from a source to a target folder
* **getconfigurationproperties** lists the name/value pairs of a configuration
* **testfolderaccess** tests if a user or group has access to a folder
* **createbinarybackup.py** creates a binary backup job
* **createdomain.py** creates an authentication domain
* **listrules.py** list authorization rules subset on a principal and/or a uri
* **loginviauthinfo.py** use an authinfo file to authenticate to the CLI
* **updateprefences.py** update preferences for a user or group of users
* **updatedomain.py** Load a set of userids and passwords to a Viya domain from a csv file
* **createfolders.py** Create a set of Viya folders from a csv file
* **explainaccess.py** Explains access for a folder, object or service endpoint
Check back for additional tools and if you build a tool feel free to contribute it to the collection.
## Examples
The example calls assume you are in the directory where you installed the tools. If you are not you can prepemd the directory to the filename, for example /opt/pyviyatools/callrestapi.py
If you are using the tool on windows the syntax is:
*python toolname.py*
The following examples are Linux.
\# Login to Viya using default authinfo file ~/.authinfo
*./loginviauthinfo.py*
\# Login to Viya using an authinfo file
*./loginviauthinfo.py -f ~/.authinfo_geladm*
\#return all the rest calls that can be made to the folders endpoint
*./callrestapi.py -e /folders -m get*
\#return the json for all the folders/folders
*./callrestapi.py -e /folders/folders -m get*
\#return simple text for all the folders/folders
*./callrestapi.py -e /folders/folders -m get -o simple*
\#rest calls often limit the results returned the text output will tell you returned and total available items
\#in this call set a limit above the total items to see everything
*./callrestapi.py -e /folders/folders?limit=500 -m get -o simple*
\#return the json for all the identities
*./callrestapi.py -e /identities/identities -m get*
\#return the json for all the identities output to a file
*./callrestapi.py -e /identities/identities -m get > identities.json*
\#refresh the identities cache
*./callrestapi.py -e /identities/userCount -m get*
*./callrestapi.py -e /identities/cache/refreshes -m post*
\# pass the folder path and return the folder id and uri
*./getfolderid.py -f /gelcontent*
\# delete a folder based on its path
*./deletefolder.py -f /gelcontent*
\#return a set of configuration properties
*./getconfigurationproperties.py -c sas.identities.providers.ldap.user -o simple*
\#create a domain using createdomain
*./createdomain.py -t password -d test -u sasadm -p lnxsas -g "SASAdministrators,HR ,Sales"*
\#Update an existing domain to add credentials from a csv file
*./updatedomain.py -d LASRAuth -f /tmp/myusers.csv*
csv file format
no header row
column1 is userid
column2 is password
column3 is identity
column4 is identity type (user or group)
For example:
myuserid,mypass,Sales,group
acct1,pw1,sasadm,user
\#create a domain using callrestapi. Last part of endpoint is domain name
*./callrestapi.py -e /credentials/domains/\<newdomain\> -m post -i domain.json*
INPUT JSON must be formatted as the endpoint expects. Example:
{
"id": "\<newdomain\>",
"type": "password"
}
\#test folder access
*./root/admin/pyviyatools/testFolderAccess.py -f '/gelcontent' -p Sales -m read -s grant -q*
\#return basic content using accept response type
*./callrestapi.py -m get -e /identities/users/sasadm -a "application/vnd.sas.identity.basic+json"*
\# Display all sasadministrator rules
*./listrules.py --p SASadministrators -o simple*
\# Display all rules that contain SASVisual in the URI
*./listrules.py -u SASVisual -o simple*
\# Create folders from a CSV file
*./createfolders.py" -f /tmp/newfolders.csv*
FORMAT OF CSV file folder path (parents must exist), description
/RnD, Folder under root for R&D
/RnD/reports, reports
/RnD/analysis, analysis
/RnD/data plans, data plans
/temp,My temporary folder
/temp/mystuff, sub-folder
\# Update the theme for the user sasadm
*./updatepreferences.py -t user -tn sasadm -pi OpenUI.Theme.Default -pv sas_hcb*
\#Explain direct and indirect permissions on the folder /folderA/folderB, no header row. For folders, conveyed permissions are shown by default.
*./explainaccess.py -f /folderA/folderB*
\#As above but for a specific user named Heather
*./explainaccess.py -f /folderA/folderB -n Heather -t user*
\#As above with a header row
*./explainaccess.py -f /folderA/folderB --header*
\#As above with a header row and the folder path, which is useful if you concatenate sets of results in one file
*./explainaccess.py -f /folderA/folderB -p --header*
\#As above showing only rows which include a direct grant or prohibit
*./explainaccess.py -f /folderA/folderB --direct_only*
\#Explain direct and indirect permissions on a service endpoint. Note in the results that there are no conveyed permissions. By default they are not shown for URIs.
*./explainaccess.py -u /SASEnvironmentManager/dashboard*
\#As above but including a header row and the create permission, which is relevant for services but not for folders and other objects
*./explainaccess.py -u /SASEnvironmentManager/dashboard --header -l read update delete secure add remove create*
\#Explain direct and indirect permissions on a report, reducing the permissions reported to just read, update, delete and secure, since none of add, remove or create are applicable to a report.
*./explainaccess.py -u /reports/reports/*folder_id *--header -l read update delete secure*
\#Explain direct and indirect permissions on a folder expressed as a URI. Keep the default permissions list, but for completeness we must also specify -c true to request conveyed permissions be displayed, as they are not displayed by default for URIs.
*./explainaccess.py -u /folders/folders/*folder_id *--header -p -c true*
**Troubleshooting**
The most common problem is an expired access token. You may see a message like:
{"errorCode":0,"message":"Cannot convert access token to JSON","details":["traceId: 6bca23b2b3a2cfda","path: /folders/folders"],"remediation":null,"links":[ ],"version":2,"httpStatusCode":401}
To fix the problem generate a new access token /opt/sas/viya/home/bin/sas-admin auth login
To see the current setup, including python and package versions, environment variable settings, profile information and current user. Run
*python showsetup.py*
If you get this error : Login failed. Error with security certificate.
Set the environment variable for the SSL certificate file. For example:
export SSL_CERT_FILE=/opt/sas/viya/config/etc/SASSecurityCertificateFramework/cacerts/trustedcerts.pem
If you get this error:
Raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='intviya01.race.sas.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, u'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:579)'),))
Set the environment variable for the SSL certificate file. For example:
export REQUESTS_CA_BUNDLE=/opt/sas/viya/config/etc/SASSecurityCertificateFramework/cacerts/trustedcerts.pem
## Developing with the existing functions
The file sharedfunctions.py contains a set of generic functions that make it easy to build additional tools.
*callrestapi* is the main function, called by many other programs and by the callrestapi program to make the REST calls. It accepts as parameters:
* reqval: the request value which is the rest endpoint for the request
* reqtype: the type of REST request, get,put, post, delete
* acceptType: optinal accept type content header
* contentType: optional content type content header
* data: optionally a python dictionary created from the json for the rest request
* stoponerror: whether the function will stop all further processing if an error occurs (default 0 to not stop)
## Contributing
We welcome your contributions! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to submit contributions to this project.
## License
This project is licensed under the [Apache 2.0 License](LICENSE).

184
createfolders.py

@ -1,93 +1,93 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# createfolders.py
# October 2018
#
#
# Change History
#
# 30oct2018 Initial development
#
# Format of csv file is two columns
# Column 1 is the full path to the folder
# Column 2 is a description
#
# For example:
#/RnD, Folder under root for R&D
#/RnD/reports, reports
#/RnD/analysis, analysis
#/RnD/data plans, data plans
#/temp,My temporary folder
#
# Copyright 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import argparse
import csv
import os
from sharedfunctions import callrestapi, getfolderid, file_accessible
# setup command-line arguements
parser = argparse.ArgumentParser(description="Create folders that are read from a csv file")
parser.add_argument("-f","--file", help="Full path to csv file containing folders, format of csv: 'folderpath,description ",required='True')
args = parser.parse_args()
file=args.file
reqtype="post"
check=file_accessible(file,'r')
# file can be read
if check:
with open(file, 'rt') as f:
filecontents = csv.reader(f)
for row in filecontents:
#print(row)
newfolder=row[0]
description=row[1]
if newfolder[0]!='/': newfolder="/"+newfolder
folder=os.path.basename(os.path.normpath(newfolder))
parent_folder=os.path.dirname(newfolder)
data = {}
data['name'] = folder
data['description'] = description
print ("Creating folder "+newfolder )
if parent_folder=="/": reqval='/folders/folders'
else: # parent folder create a child
parentinfo=getfolderid(parent_folder)
if parentinfo != None:
parenturi=parentinfo[1]
reqval='/folders/folders?parentFolderUri='+parenturi
else: print("Parent folder not found")
myresult=callrestapi(reqval,reqtype,data=data,stoponerror=0)
else:
print("ERROR: cannot read "+file)
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# createfolders.py
# October 2018
#
#
# Change History
#
# 30oct2018 Initial development
#
# Format of csv file is two columns
# Column 1 is the full path to the folder
# Column 2 is a description
#
# For example:
#/RnD, Folder under root for R&D
#/RnD/reports, reports
#/RnD/analysis, analysis
#/RnD/data plans, data plans
#/temp,My temporary folder
#
# Copyright 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import argparse
import csv
import os
from sharedfunctions import callrestapi, getfolderid, file_accessible
# setup command-line arguements
parser = argparse.ArgumentParser(description="Create folders that are read from a csv file")
parser.add_argument("-f","--file", help="Full path to csv file containing folders, format of csv: 'folderpath,description ",required='True')
args = parser.parse_args()
file=args.file
reqtype="post"
check=file_accessible(file,'r')
# file can be read
if check:
with open(file, 'rt') as f:
filecontents = csv.reader(f)
for row in filecontents:
#print(row)
newfolder=row[0]
description=row[1]
if newfolder[0]!='/': newfolder="/"+newfolder
folder=os.path.basename(os.path.normpath(newfolder))
parent_folder=os.path.dirname(newfolder)
data = {}
data['name'] = folder
data['description'] = description
print ("Creating folder "+newfolder )
if parent_folder=="/": reqval='/folders/folders'
else: # parent folder create a child
parentinfo=getfolderid(parent_folder)
if parentinfo != None:
parenturi=parentinfo[1]
reqval='/folders/folders?parentFolderUri='+parenturi
else: print("Parent folder not found")
myresult=callrestapi(reqval,reqtype,data=data,stoponerror=0)
else:
print("ERROR: cannot read "+file)

586
explainaccess.py

@ -1,293 +1,293 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# explainaccess.py
# November 2018
#
# Usage:
# explainaccess.py [-f folderpath | -u objectURI] [-n name_of_user_or_group -t user|group] [-p] [--header] [--direct_only] [-l permissions_list] [-c true|false] [-d]
#
# Examples:
#
# 1. Explain direct and indirect permissions on the folder /folderA/folderB, no header row. For folders, conveyed permissions are shown by default.
# ./explainaccess.py -f /folderA/folderB
#
# 2. As 1. but for a specific user named Heather
# ./explainaccess.py -f /folderA/folderB -n Heather -t user
#
# 3. As 1. with a header row
# ./explainaccess.py -f /folderA/folderB --header
#
# 4. As 1. with a header row and the folder path, which is useful if you concatenate sets of results in one file
# ./explainaccess.py -f /folderA/folderB -p --header
#
# 5. As 1. showing only rows which include a direct grant or prohibit
# ./explainaccess.py -f /folderA/folderB --direct_only
#
# 6. Explain direct and indirect permissions on a service endpoint. Note in the results that there are no conveyed permissions.
# By default they are not shown for URIs.
# ./explainaccess.py -u /SASEnvironmentManager/dashboard
#
# 7. As 6. but including a header row and the create permission, which is relevant for services but not for folders and other objects
# ./explainaccess.py -u /SASEnvironmentManager/dashboard --header -l read update delete secure add remove create
#
# 8. Explain direct and indirect permissions on a report, reducing the permissions reported to just read, update, delete and secure,
# since none of add, remove or create are applicable to a report.
# ./explainaccess.py -u /reports/reports/e2e0e601-b5a9-4601-829a-c5137f7441c6 --header -l read update delete secure
#
# 9. Explain direct and indirect permissions on a folder expressed as a URI. Keep the default permissions list, but for completeness
# we must also specify -c true to request conveyed permissions be displayed, as they are not displayed by default for URIs.
# ./explainaccess.py -u /folders/folders/9145d26a-2c0d-4523-8835-ad186bb57fa6 --header -p -c true
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
clidir='/opt/sas/viya/home/bin/'
debug=False
direct_only=False
valid_permissions=['read','update','delete','secure','add','remove','create']
default_permissions=['read','update','delete','secure','add','remove']
#direct_permission_suffix=u"\u2666" #Black diamond suit symbol - ok in stdout, seems to cause problems with other tools
direct_permission_suffix='*'
# Import Python modules
import argparse
import subprocess
import json
import sys
from sharedfunctions import getfolderid,callrestapi
# Define exception handler so that we only output trace info from errors when in debug mode
def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook):
if debug:
debug_hook(exception_type, exception, traceback)
else:
print "%s: %s" % (exception_type.__name__, exception)
sys.excepthook = exception_handler
parser = argparse.ArgumentParser()
parser.add_argument("-f","--folderpath", help="Path to a Viya folder. You must specify either -f folderpath or -u objectURI.")
parser.add_argument("-u","--objecturi", help="Object URI. You must specify either -f folderpath or -u objectURI.")
parser.add_argument("-n","--name", help="Enter the name of the user or group to test.")
parser.add_argument("-t","--principaltype", help="Enter the type of principal to test: user or group.",choices=['user','group'])
parser.add_argument("-p","--printpath", action='store_true', help="Print the folder path in each row")
parser.add_argument("--header", action='store_true', help="Print a header row")
parser.add_argument("--direct_only", action='store_true', help="Show only explanations which include a direct grant or prohibit")
parser.add_argument("-l","--permissions_list", nargs="+", help="List of permissions, to include instead of all seven by default", default=default_permissions)
parser.add_argument("-c","--convey", help="Show conveyed permissions in results. True by default when folder path is specified. False by dfefault if Object URI is specified.",choices=['true','false'])
parser.add_argument("-d","--debug", action='store_true', help="Debug")
args = parser.parse_args()
path_to_folder=args.folderpath
objecturi=args.objecturi
name=args.name
principaltype=args.principaltype
printpath=args.printpath
header=args.header
direct_only=args.direct_only
permissions=args.permissions_list
conveyparam=args.convey
debug=args.debug
if path_to_folder and objecturi:
raise Exception('You must specify either -f and a Viya folder path, or -u and an object URI, but not both.')
if path_to_folder is None and objecturi is None:
raise Exception('You must specify either -f and a Viya folder path, or -u and an object URI. You may not specify both.')
if name and principaltype is None:
raise Exception('If you specify a principal name, you must also specify a principal type which can be user or group.')
if principaltype and name is None:
raise Exception('If you specify a principal type, you must also specify a principal name.')
for permission in permissions:
if permission not in valid_permissions:
raise Exception(permission+' is not the name of a permission. Valid permissions are: '+' '.join(map(str, valid_permissions)))
# Two ways this program can be used: for a folder, or for a URI.
if path_to_folder:
getfolderid_result_json=getfolderid(path_to_folder)
if (debug):
print(getfolderid_result_json)
if getfolderid_result_json[0] is not None:
folder_uri=getfolderid_result_json[1]
if (debug):
print("Id = "+getfolderid_result_json[0])
print("URI = "+folder_uri)
print("Path = "+getfolderid_result_json[2])
explainuri=folder_uri
resultpath=path_to_folder
#Set convey to true, unless user overrode that setting and asked for false
if(conveyparam is not None and conveyparam.lower()=='false'):
convey=False
else:
convey=True
else:
explainuri=objecturi
# This tool explains the permissions of any object.
# If the object is a folder, we expect the user to supply path_to_folder, and we find its ID
# If the object is something else, we don't have the path to the object.
# It might be possible to get the path to the object from it's ID, but I'm not sure if there is a universal way to do that.
# If the object is a report, you can call e.g.
# /opt/sas/viya/home/bin/sas-admin --output text reports show-info -id 43de1f98-d7ef-4490-bb46-cc177f995052
# And the folder is one of the results passed back. But that call uses the reports plug-in to sas-admin and
# should not be expected to return the path to other objects.
# Plus, some objects do not have a path: service endpoints, for example.
# This is a possible area for future improvement.
resultpath=objecturi
#Set convey to false, unless user overrode that setting and asked for true
if(conveyparam is not None and conveyparam.lower()=='true'):
convey=True
else:
convey=False
#Use the /authorization/decision endpoint to ask for an explanation of the rules that are relevant to principals on this URI
#See Authorization API documentation in swagger at http://swagger.na.sas.com/apis/authorization/v4/apidoc.html#op:createExplanation
endpoint='/authorization/decision'
if name and principaltype:
if(principaltype.lower()=='user'):
endpoint=endpoint+'?additionalUser='+name
else:
endpoint=endpoint+'?additionalGroup='+name
method='post'
accept='application/vnd.sas.authorization.explanations+json'
content='application/vnd.sas.selection+json'
inputdata={"resources":[explainuri]}
decisions_result_json=callrestapi(endpoint,method,accept,content,inputdata)
#print(decisions_result_json)
#print('decisions_result_json is a '+type(decisions_result_json).__name__+' object') #decisions_result_json is a dict object
e = decisions_result_json['explanations'][explainuri]
#print('e is a '+type(e).__name__+' object') #e is a list object
# Print header row if header argument was specified
if header:
if printpath:
if convey:
print('path,principal,'+','.join(map(str, permissions))+','+','.join(map('{0}(convey)'.format, permissions)))
else:
print('path,principal,'+','.join(map(str, permissions)))
else:
if convey:
print('principal,'+','.join(map(str, permissions))+','+','.join(map('{0}(convey)'.format, permissions)))
else:
print('principal,'+','.join(map(str, permissions)))
principal_found=False
#For each principle's section in the explanations section of the data returned from the REST API call...
for pi in e:
#print pi['principal']
#We are starting a new principal, so initialise some variables for this principal
outstr=''
has_a_direct_grant_or_deny=False
if printpath:
outstr=outstr+resultpath+','
# If a name and principaltype are provided as arguments, we will only output a row for that principal
if name and principaltype:
if 'name' in pi['principal']:
if (pi['principal']['name'].lower() == name.lower()):
principal_found=True
outstr=outstr+pi['principal']['name']
# Permissions on object
for permission in permissions:
# Not all objects have all the permissions
# Note that some objects do have permissions which are not meaningful for that object.
# E.g. SASAdministrators are granted Add and Remove on reports, by an OOTB rule which grants SASAdministrators all permissions (including Add and Remove) on /**.
# Meanwhile, Add and Remove are not shown in the View or Edit Authotizations dialogs for reports in EV etc.
# So, while it may be correct for the /authorization/decisions endpoint to explain that SASAdministrators are granted Add and Remove on a report,
# that does not alter the fact that in the context of a report, Add and Remove permissions are not meaningful.
if pi[permission.lower()]:
# This permission was in the expanation for this principal
outstr=outstr+','+pi[permission.lower()]['result']
if 'grantFactor' in pi[permission.lower()]:
if 'direct' in pi[permission.lower()]['grantFactor']:
if pi[permission.lower()]['grantFactor']['direct']:
has_a_direct_grant_or_deny=True
outstr=outstr+direct_permission_suffix
else:
# This permission was absent from the expanation for this principal
outstr=outstr+','
# Conveyed permissions
if convey:
for permission in permissions:
# Only a few objects have conveyed permissions at all
if 'conveyedExplanation' in pi[permission.lower()]:
# This permission was in the expanation for this principal
outstr=outstr+','+pi[permission.lower()]['conveyedExplanation']['result']
if 'grantFactor' in pi[permission.lower()]['conveyedExplanation']:
if 'direct' in pi[permission.lower()]['conveyedExplanation']['grantFactor']:
if pi[permission.lower()]['conveyedExplanation']['grantFactor']['direct']:
has_a_direct_grant_or_deny=True
outstr=outstr+direct_permission_suffix
else:
# This permission was absent from the expanation for this principal
outstr=outstr+','
if direct_only:
if has_a_direct_grant_or_deny:
print(outstr)
else:
print(outstr)
# But if no name or principaltype are provided, we output all rows
else:
if 'name' in pi['principal']:
outstr=outstr+pi['principal']['name']
else:
outstr=outstr+pi['principal']['type']
# Permissions on object
for permission in permissions:
# Not all objects have all the permissions
if pi[permission.lower()]:
# This permission was in the expanation for this principal
outstr=outstr+','+pi[permission.lower()]['result']
if 'grantFactor' in pi[permission.lower()]:
if 'direct' in pi[permission.lower()]['grantFactor']:
if pi[permission.lower()]['grantFactor']['direct']:
has_a_direct_grant_or_deny=True
outstr=outstr+direct_permission_suffix
else:
# This permission was absent from the expanation for this principal
outstr=outstr+','
# Conveyed permissions
if convey:
for permission in permissions:
# Not all objects have all the permissions
if 'conveyedExplanation' in pi[permission.lower()]:
# This permission was in the expanation for this principal
outstr=outstr+','+pi[permission.lower()]['conveyedExplanation']['result']
if 'grantFactor' in pi[permission.lower()]['conveyedExplanation']:
if 'direct' in pi[permission.lower()]['conveyedExplanation']['grantFactor']:
if pi[permission.lower()]['conveyedExplanation']['grantFactor']['direct']:
has_a_direct_grant_or_deny=True
outstr=outstr+direct_permission_suffix
else:
# This permission was absent from the expanation for this principal
outstr=outstr+','
if direct_only:
if has_a_direct_grant_or_deny:
print(outstr)
else:
print(outstr)
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# explainaccess.py
# November 2018
#
# Usage:
# explainaccess.py [-f folderpath | -u objectURI] [-n name_of_user_or_group -t user|group] [-p] [--header] [--direct_only] [-l permissions_list] [-c true|false] [-d]
#
# Examples:
#
# 1. Explain direct and indirect permissions on the folder /folderA/folderB, no header row. For folders, conveyed permissions are shown by default.
# ./explainaccess.py -f /folderA/folderB
#
# 2. As 1. but for a specific user named Heather
# ./explainaccess.py -f /folderA/folderB -n Heather -t user
#
# 3. As 1. with a header row
# ./explainaccess.py -f /folderA/folderB --header
#
# 4. As 1. with a header row and the folder path, which is useful if you concatenate sets of results in one file
# ./explainaccess.py -f /folderA/folderB -p --header
#
# 5. As 1. showing only rows which include a direct grant or prohibit
# ./explainaccess.py -f /folderA/folderB --direct_only
#
# 6. Explain direct and indirect permissions on a service endpoint. Note in the results that there are no conveyed permissions.
# By default they are not shown for URIs.
# ./explainaccess.py -u /SASEnvironmentManager/dashboard
#
# 7. As 6. but including a header row and the create permission, which is relevant for services but not for folders and other objects
# ./explainaccess.py -u /SASEnvironmentManager/dashboard --header -l read update delete secure add remove create
#
# 8. Explain direct and indirect permissions on a report, reducing the permissions reported to just read, update, delete and secure,
# since none of add, remove or create are applicable to a report.
# ./explainaccess.py -u /reports/reports/e2e0e601-b5a9-4601-829a-c5137f7441c6 --header -l read update delete secure
#
# 9. Explain direct and indirect permissions on a folder expressed as a URI. Keep the default permissions list, but for completeness
# we must also specify -c true to request conveyed permissions be displayed, as they are not displayed by default for URIs.
# ./explainaccess.py -u /folders/folders/9145d26a-2c0d-4523-8835-ad186bb57fa6 --header -p -c true
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
clidir='/opt/sas/viya/home/bin/'
debug=False
direct_only=False
valid_permissions=['read','update','delete','secure','add','remove','create']
default_permissions=['read','update','delete','secure','add','remove']
#direct_permission_suffix=u"\u2666" #Black diamond suit symbol - ok in stdout, seems to cause problems with other tools
direct_permission_suffix='*'
# Import Python modules
import argparse
import subprocess
import json
import sys
from sharedfunctions import getfolderid,callrestapi
# Define exception handler so that we only output trace info from errors when in debug mode
def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook):
if debug:
debug_hook(exception_type, exception, traceback)
else:
print "%s: %s" % (exception_type.__name__, exception)
sys.excepthook = exception_handler
parser = argparse.ArgumentParser()
parser.add_argument("-f","--folderpath", help="Path to a Viya folder. You must specify either -f folderpath or -u objectURI.")
parser.add_argument("-u","--objecturi", help="Object URI. You must specify either -f folderpath or -u objectURI.")
parser.add_argument("-n","--name", help="Enter the name of the user or group to test.")
parser.add_argument("-t","--principaltype", help="Enter the type of principal to test: user or group.",choices=['user','group'])
parser.add_argument("-p","--printpath", action='store_true', help="Print the folder path in each row")
parser.add_argument("--header", action='store_true', help="Print a header row")
parser.add_argument("--direct_only", action='store_true', help="Show only explanations which include a direct grant or prohibit")
parser.add_argument("-l","--permissions_list", nargs="+", help="List of permissions, to include instead of all seven by default", default=default_permissions)
parser.add_argument("-c","--convey", help="Show conveyed permissions in results. True by default when folder path is specified. False by dfefault if Object URI is specified.",choices=['true','false'])
parser.add_argument("-d","--debug", action='store_true', help="Debug")
args = parser.parse_args()
path_to_folder=args.folderpath
objecturi=args.objecturi
name=args.name
principaltype=args.principaltype
printpath=args.printpath
header=args.header
direct_only=args.direct_only
permissions=args.permissions_list
conveyparam=args.convey
debug=args.debug
if path_to_folder and objecturi:
raise Exception('You must specify either -f and a Viya folder path, or -u and an object URI, but not both.')
if path_to_folder is None and objecturi is None:
raise Exception('You must specify either -f and a Viya folder path, or -u and an object URI. You may not specify both.')
if name and principaltype is None:
raise Exception('If you specify a principal name, you must also specify a principal type which can be user or group.')
if principaltype and name is None:
raise Exception('If you specify a principal type, you must also specify a principal name.')
for permission in permissions:
if permission not in valid_permissions:
raise Exception(permission+' is not the name of a permission. Valid permissions are: '+' '.join(map(str, valid_permissions)))
# Two ways this program can be used: for a folder, or for a URI.
if path_to_folder:
getfolderid_result_json=getfolderid(path_to_folder)
if (debug):
print(getfolderid_result_json)
if getfolderid_result_json[0] is not None:
folder_uri=getfolderid_result_json[1]
if (debug):
print("Id = "+getfolderid_result_json[0])
print("URI = "+folder_uri)
print("Path = "+getfolderid_result_json[2])
explainuri=folder_uri
resultpath=path_to_folder
#Set convey to true, unless user overrode that setting and asked for false
if(conveyparam is not None and conveyparam.lower()=='false'):
convey=False
else:
convey=True
else:
explainuri=objecturi
# This tool explains the permissions of any object.
# If the object is a folder, we expect the user to supply path_to_folder, and we find its ID
# If the object is something else, we don't have the path to the object.
# It might be possible to get the path to the object from it's ID, but I'm not sure if there is a universal way to do that.
# If the object is a report, you can call e.g.
# /opt/sas/viya/home/bin/sas-admin --output text reports show-info -id 43de1f98-d7ef-4490-bb46-cc177f995052
# And the folder is one of the results passed back. But that call uses the reports plug-in to sas-admin and
# should not be expected to return the path to other objects.
# Plus, some objects do not have a path: service endpoints, for example.
# This is a possible area for future improvement.
resultpath=objecturi
#Set convey to false, unless user overrode that setting and asked for true
if(conveyparam is not None and conveyparam.lower()=='true'):
convey=True
else:
convey=False
#Use the /authorization/decision endpoint to ask for an explanation of the rules that are relevant to principals on this URI
#See Authorization API documentation in swagger at http://swagger.na.sas.com/apis/authorization/v4/apidoc.html#op:createExplanation
endpoint='/authorization/decision'
if name and principaltype:
if(principaltype.lower()=='user'):
endpoint=endpoint+'?additionalUser='+name
else:
endpoint=endpoint+'?additionalGroup='+name
method='post'
accept='application/vnd.sas.authorization.explanations+json'
content='application/vnd.sas.selection+json'
inputdata={"resources":[explainuri]}
decisions_result_json=callrestapi(endpoint,method,accept,content,inputdata)
#print(decisions_result_json)
#print('decisions_result_json is a '+type(decisions_result_json).__name__+' object') #decisions_result_json is a dict object
e = decisions_result_json['explanations'][explainuri]
#print('e is a '+type(e).__name__+' object') #e is a list object
# Print header row if header argument was specified
if header:
if printpath:
if convey:
print('path,principal,'+','.join(map(str, permissions))+','+','.join(map('{0}(convey)'.format, permissions)))
else:
print('path,principal,'+','.join(map(str, permissions)))
else:
if convey:
print('principal,'+','.join(map(str, permissions))+','+','.join(map('{0}(convey)'.format, permissions)))
else:
print('principal,'+','.join(map(str, permissions)))
principal_found=False
#For each principle's section in the explanations section of the data returned from the REST API call...
for pi in e:
#print pi['principal']
#We are starting a new principal, so initialise some variables for this principal
outstr=''
has_a_direct_grant_or_deny=False
if printpath:
outstr=outstr+resultpath+','
# If a name and principaltype are provided as arguments, we will only output a row for that principal
if name and principaltype:
if 'name' in pi['principal']:
if (pi['principal']['name'].lower() == name.lower()):
principal_found=True
outstr=outstr+pi['principal']['name']
# Permissions on object
for permission in permissions:
# Not all objects have all the permissions
# Note that some objects do have permissions which are not meaningful for that object.
# E.g. SASAdministrators are granted Add and Remove on reports, by an OOTB rule which grants SASAdministrators all permissions (including Add and Remove) on /**.
# Meanwhile, Add and Remove are not shown in the View or Edit Authotizations dialogs for reports in EV etc.
# So, while it may be correct for the /authorization/decisions endpoint to explain that SASAdministrators are granted Add and Remove on a report,
# that does not alter the fact that in the context of a report, Add and Remove permissions are not meaningful.
if pi[permission.lower()]:
# This permission was in the expanation for this principal
outstr=outstr+','+pi[permission.lower()]['result']
if 'grantFactor' in pi[permission.lower()]:
if 'direct' in pi[permission.lower()]['grantFactor']:
if pi[permission.lower()]['grantFactor']['direct']:
has_a_direct_grant_or_deny=True
outstr=outstr+direct_permission_suffix
else:
# This permission was absent from the expanation for this principal
outstr=outstr+','
# Conveyed permissions
if convey:
for permission in permissions:
# Only a few objects have conveyed permissions at all
if 'conveyedExplanation' in pi[permission.lower()]:
# This permission was in the expanation for this principal
outstr=outstr+','+pi[permission.lower()]['conveyedExplanation']['result']
if 'grantFactor' in pi[permission.lower()]['conveyedExplanation']:
if 'direct' in pi[permission.lower()]['conveyedExplanation']['grantFactor']:
if pi[permission.lower()]['conveyedExplanation']['grantFactor']['direct']:
has_a_direct_grant_or_deny=True
outstr=outstr+direct_permission_suffix
else:
# This permission was absent from the expanation for this principal
outstr=outstr+','
if direct_only:
if has_a_direct_grant_or_deny:
print(outstr)
else:
print(outstr)
# But if no name or principaltype are provided, we output all rows
else:
if 'name' in pi['principal']:
outstr=outstr+pi['principal']['name']
else:
outstr=outstr+pi['principal']['type']
# Permissions on object
for permission in permissions:
# Not all objects have all the permissions
if pi[permission.lower()]:
# This permission was in the expanation for this principal
outstr=outstr+','+pi[permission.lower()]['result']
if 'grantFactor' in pi[permission.lower()]:
if 'direct' in pi[permission.lower()]['grantFactor']:
if pi[permission.lower()]['grantFactor']['direct']:
has_a_direct_grant_or_deny=True
outstr=outstr+direct_permission_suffix
else:
# This permission was absent from the expanation for this principal
outstr=outstr+','
# Conveyed permissions
if convey:
for permission in permissions:
# Not all objects have all the permissions
if 'conveyedExplanation' in pi[permission.lower()]:
# This permission was in the expanation for this principal
outstr=outstr+','+pi[permission.lower()]['conveyedExplanation']['result']
if 'grantFactor' in pi[permission.lower()]['conveyedExplanation']:
if 'direct' in pi[permission.lower()]['conveyedExplanation']['grantFactor']:
if pi[permission.lower()]['conveyedExplanation']['grantFactor']['direct']:
has_a_direct_grant_or_deny=True
outstr=outstr+direct_permission_suffix
else:
# This permission was absent from the expanation for this principal
outstr=outstr+','
if direct_only:
if has_a_direct_grant_or_deny:
print(outstr)
else:
print(outstr)

248
listrules.py

@ -1,124 +1,124 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# listrules.py
# August 2018
#
# listrulesforidentity
#
# Change History
# December 2018 - Added custom CSV output code, which writes out consistent columns in a specific order for the result rules JSON
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import argparse
from sharedfunctions import callrestapi, printresult
# setup command-line arguements
parser = argparse.ArgumentParser(description="List rules for a principal and/or an endpoint")
parser.add_argument("-u","--uri", help="Enter a string that the objecturi contains.",default="none")
parser.add_argument("-p","--principal", help="Enter the identity name or authenticatedUsers, everyone or guest",default='none')
parser.add_argument("-o","--output", help="Output Style", choices=['csv','json','simple'],default='json')
args = parser.parse_args()
objuri=args.uri
ident=args.principal
output_style=args.output
# set the limit high so that all data is returned
limitval=10000
# Define columns we want to output for each rule item (whether the item has a value for that column or not)
desired_output_columns=['objectUri','containerUri','principalType','principal','setting','permissions','description','reason','createdBy','createdTimestamp','modifiedBy','modifiedTimestamp','condition','matchParams','mediaType','enabled','version']
valid_permissions=['read','update','delete','secure','add','remove','create']
# build the request depending on what options were passed in
if ident.lower()=='authenticatedusers': ident='authenticatedUsers'
if ident=='none' and objuri=='none': reqval= "/authorization/rules"
elif ident=='none' and objuri != 'none': reqval= "/authorization/rules?filter=contains(objectUri,'"+objuri+"')"
elif ident!='none' and objuri == 'none':
if ident=='guest' or ident=='everyone' or ident=='authenticatedUsers':
reqval= "/authorization/rules?filter=eq(principalType,'"+ident+"')"
else:
reqval= "/authorization/rules?filter=eq(principal,'"+ident+"')"
elif ident!='none' and objuri != 'none':
if ident=='guest' or ident=='everyone' or ident=='authenticatedUsers':
reqval= "/authorization/rules?filter=and(eq(principalType,'"+ident+"'),contains(objectUri,'"+objuri+"'))"
else:
reqval= "/authorization/rules?filter=and(eq(principal,'"+ident+"'),contains(objectUri,'"+objuri+"'))"
if ident=='none' and objuri=='none': reqval=reqval+'?limit='+str(limitval)
else: reqval=reqval+'&limit='+str(limitval)
reqtype='get'
#make the rest call
rules_result_json=callrestapi(reqval,reqtype)
#print(rules_result_json)
#print('rules_result_json is a '+type(rules_result_json).__name__+' object') #rules_result_json is a dict object
#print the result if output style is json or simple
if output_style in ['json','simple']:
printresult(rules_result_json,output_style)
elif output_style=='csv':
# Print a header row
print(','.join(map(str,desired_output_columns)))
if 'items' in rules_result_json:
#print "There are " + str(rules_result_json['count']) + " rules"
for item in rules_result_json['items']:
outstr=''
#print(item)
for column in desired_output_columns:
# Add a comma to the output string, even if we will not output anything else, unless this is the very first desired output column
if column is not desired_output_columns[0]: outstr=outstr+','
if column=='setting':
# The setting value is derived from two columns: type and condition.
if 'condition' in item:
#print("Condition found")
outstr=outstr+'conditional '+item['type']
else:
outstr=outstr+item['type']
elif column in item:
# This column is in the results item for this rule
# Most columns are straight strings, but a few need special handling
if column in ['condition','description','reason']:
# The these strings can have values whcih contain commas, need we to quote them to avoid the commas being interpreted as column separators in the CSV
outstr=outstr+'"'+item[column]+'"'
elif column=='permissions':
# Construct a string listing each permission in the correct order, separated by spaces and surrounded by square brackets
outstr=outstr+'['
permstr=''
# Output permissions in the order we choose, not the order they appear in the result item
for permission in valid_permissions:
for result_permission in item['permissions']:
if permission == result_permission:
# Add a space to separate permissions if this isn't the first permission
if not permstr=='': permstr=permstr+' '
permstr=permstr+result_permission
outstr=outstr+permstr+']'
else:
# Normal column
# Some columns contain non-string values: matchParams and enabled are boolean, version is integer. Convert everything to a string.
outstr=outstr+str(item[column])
print(outstr)
else:
print "output_style can be json, simple or csv. You specified " + output_style + " which is invalid."
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# listrules.py
# August 2018
#
# listrulesforidentity
#
# Change History
# December 2018 - Added custom CSV output code, which writes out consistent columns in a specific order for the result rules JSON
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import argparse
from sharedfunctions import callrestapi, printresult
# setup command-line arguements
parser = argparse.ArgumentParser(description="List rules for a principal and/or an endpoint")
parser.add_argument("-u","--uri", help="Enter a string that the objecturi contains.",default="none")
parser.add_argument("-p","--principal", help="Enter the identity name or authenticatedUsers, everyone or guest",default='none')
parser.add_argument("-o","--output", help="Output Style", choices=['csv','json','simple'],default='json')
args = parser.parse_args()
objuri=args.uri
ident=args.principal
output_style=args.output
# set the limit high so that all data is returned
limitval=10000
# Define columns we want to output for each rule item (whether the item has a value for that column or not)
desired_output_columns=['objectUri','containerUri','principalType','principal','setting','permissions','description','reason','createdBy','createdTimestamp','modifiedBy','modifiedTimestamp','condition','matchParams','mediaType','enabled','version']
valid_permissions=['read','update','delete','secure','add','remove','create']
# build the request depending on what options were passed in
if ident.lower()=='authenticatedusers': ident='authenticatedUsers'
if ident=='none' and objuri=='none': reqval= "/authorization/rules"
elif ident=='none' and objuri != 'none': reqval= "/authorization/rules?filter=contains(objectUri,'"+objuri+"')"
elif ident!='none' and objuri == 'none':
if ident=='guest' or ident=='everyone' or ident=='authenticatedUsers':
reqval= "/authorization/rules?filter=eq(principalType,'"+ident+"')"
else:
reqval= "/authorization/rules?filter=eq(principal,'"+ident+"')"
elif ident!='none' and objuri != 'none':
if ident=='guest' or ident=='everyone' or ident=='authenticatedUsers':
reqval= "/authorization/rules?filter=and(eq(principalType,'"+ident+"'),contains(objectUri,'"+objuri+"'))"
else:
reqval= "/authorization/rules?filter=and(eq(principal,'"+ident+"'),contains(objectUri,'"+objuri+"'))"
if ident=='none' and objuri=='none': reqval=reqval+'?limit='+str(limitval)
else: reqval=reqval+'&limit='+str(limitval)
reqtype='get'
#make the rest call
rules_result_json=callrestapi(reqval,reqtype)
#print(rules_result_json)
#print('rules_result_json is a '+type(rules_result_json).__name__+' object') #rules_result_json is a dict object
#print the result if output style is json or simple
if output_style in ['json','simple']:
printresult(rules_result_json,output_style)
elif output_style=='csv':
# Print a header row
print(','.join(map(str,desired_output_columns)))
if 'items' in rules_result_json:
#print "There are " + str(rules_result_json['count']) + " rules"
for item in rules_result_json['items']:
outstr=''
#print(item)
for column in desired_output_columns:
# Add a comma to the output string, even if we will not output anything else, unless this is the very first desired output column
if column is not desired_output_columns[0]: outstr=outstr+','
if column=='setting':
# The setting value is derived from two columns: type and condition.
if 'condition' in item:
#print("Condition found")
outstr=outstr+'conditional '+item['type']
else:
outstr=outstr+item['type']
elif column in item:
# This column is in the results item for this rule
# Most columns are straight strings, but a few need special handling
if column in ['condition','description','reason']:
# The these strings can have values whcih contain commas, need we to quote them to avoid the commas being interpreted as column separators in the CSV
outstr=outstr+'"'+item[column]+'"'
elif column=='permissions':
# Construct a string listing each permission in the correct order, separated by spaces and surrounded by square brackets
outstr=outstr+'['
permstr=''
# Output permissions in the order we choose, not the order they appear in the result item
for permission in valid_permissions:
for result_permission in item['permissions']:
if permission == result_permission:
# Add a space to separate permissions if this isn't the first permission
if not permstr=='': permstr=permstr+' '
permstr=permstr+result_permission
outstr=outstr+permstr+']'
else:
# Normal column
# Some columns contain non-string values: matchParams and enabled are boolean, version is integer. Convert everything to a string.
outstr=outstr+str(item[column])
print(outstr)
else:
print "output_style can be json, simple or csv. You specified " + output_style + " which is invalid."

116
loginviauthinfo.py

@ -1,59 +1,59 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# IMPORTANT: calls the sas-admin cli change the variable below if your CLI is not
# installed in the default location
#
# usage python loginviauthinfo.py
#
# Change History
#
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from __future__ import print_function
import netrc
import subprocess
import platform
import os
import argparse
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION
clidir='/opt/sas/viya/home/bin/'
#clidir='c:\\admincli\\'
# get input parameters
parser = argparse.ArgumentParser(description="Authinfo File")
parser.add_argument("-f","--file", help="Enter the path to the authinfo file.",default='.authinfo')
args = parser.parse_args()
authfile=args.file
host=platform.node()
# Read from the authinfo file in your home directory
fname=os.path.join(os.path.expanduser('~'),authfile)
cur_profile=os.environ.get("SAS_CLI_PROFILE","Default")
print("Logging in with profile: ",cur_profile )
if os.path.isfile(fname):
secrets = netrc.netrc(fname)
username, account, password = secrets.authenticators( host )
command=clidir+'sas-admin --profile '+cur_profile+ ' auth login -u '+username+ ' -p '+password
subprocess.call(command, shell=True)
else:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# IMPORTANT: calls the sas-admin cli change the variable below if your CLI is not
# installed in the default location
#
# usage python loginviauthinfo.py
#
# Change History
#
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from __future__ import print_function
import netrc
import subprocess
import platform
import os
import argparse
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION
clidir='/opt/sas/viya/home/bin/'
#clidir='c:\\admincli\\'
# get input parameters
parser = argparse.ArgumentParser(description="Authinfo File")
parser.add_argument("-f","--file", help="Enter the path to the authinfo file.",default='.authinfo')
args = parser.parse_args()
authfile=args.file
host=platform.node()
# Read from the authinfo file in your home directory
fname=os.path.join(os.path.expanduser('~'),authfile)
cur_profile=os.environ.get("SAS_CLI_PROFILE","Default")
print("Logging in with profile: ",cur_profile )
if os.path.isfile(fname):
secrets = netrc.netrc(fname)
username, account, password = secrets.authenticators( host )
command=clidir+'sas-admin --profile '+cur_profile+ ' auth login -u '+username+ ' -p '+password
subprocess.call(command, shell=True)
else:
print('ERROR: '+fname+' does not exist')

130
showsetup.py

@ -1,65 +1,65 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# showsetup.py
#
# output some system settings to help with debugging issues
#
# October 2018
#
#
# Change History
#
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import sys
import requests
import os
from sharedfunctions import getprofileinfo
# software versions
print("Python Version is: "+str(sys.version_info[0])+'.'+str(sys.version_info[1]))
print("Requests Version is: "+requests.__version__)
# profile
cur_profile=os.environ.get("SAS_CLI_PROFILE","NOTSET")
if cur_profile=="NOTSET":
print("SAS_CLI_PROFILE environment variable not set, using Default profile")
cur_profile='Default'
else:
print("SAS_CLI_PROFILE environment variable set to profile "+ cur_profile)
ssl_file=os.environ.get("SSL_CERT_FILE","NOTSET")
if ssl_file=="NOTSET":
print("SSL_CERT_FILE environment variable not set.")
else:
print("SSL_CERT_FILE environment variable set to profile "+ ssl_file)
r_ssl_file=os.environ.get("REQUESTS_CA_BUNDLE","NOTSET")
if r_ssl_file=="NOTSET":
print("REQUESTS_CA_BUNDLE environment variable not set.")
else:
print("REQUESTS_CA_BUNDLE environment variable set to profile "+ r_ssl_file)
getprofileinfo(cur_profile)
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# showsetup.py
#
# output some system settings to help with debugging issues
#
# October 2018
#
#
# Change History
#
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import sys
import requests
import os
from sharedfunctions import getprofileinfo
# software versions
print("Python Version is: "+str(sys.version_info[0])+'.'+str(sys.version_info[1]))
print("Requests Version is: "+requests.__version__)
# profile
cur_profile=os.environ.get("SAS_CLI_PROFILE","NOTSET")
if cur_profile=="NOTSET":
print("SAS_CLI_PROFILE environment variable not set, using Default profile")
cur_profile='Default'
else:
print("SAS_CLI_PROFILE environment variable set to profile "+ cur_profile)
ssl_file=os.environ.get("SSL_CERT_FILE","NOTSET")
if ssl_file=="NOTSET":
print("SSL_CERT_FILE environment variable not set.")
else:
print("SSL_CERT_FILE environment variable set to profile "+ ssl_file)
r_ssl_file=os.environ.get("REQUESTS_CA_BUNDLE","NOTSET")
if r_ssl_file=="NOTSET":
print("REQUESTS_CA_BUNDLE environment variable not set.")
else:
print("REQUESTS_CA_BUNDLE environment variable set to profile "+ r_ssl_file)
getprofileinfo(cur_profile)

278
unittestsadm33.sh

@ -1,139 +1,139 @@
#!/usr/bin/sh
#
# unittestsadm33.sh
# June 2018
#
# Calls each of the pyviyatools at least once, as a simple unit/integration test
#
# Some tests are provided with example folder paths which are not likely to
# exist in your deployment. However, most tests are not dependent on any
# custom content in the deployment, and will run well on any deployment.
#
# Some tests intentionally do things which do not work, e.g. delete a folder
# which does not exist. The error message returned by the tool called is
# considered sufficient to demonstrate that it has in fact been called, and is
# working as intended. If you like, you could create content for these tests
# to act on, e.g. create a folder called "/this_folder_does_not_exist", and
# allow one of the tests below delete it.
#
# The following tests create new content, and do not clean up after themselves:
# 1. "Create a domain using createdomain"
# - creates or replaces domain named 'test', does not create multiple
# copies
# 2. "Create a binary backup job"
# - creates a new scheduled job named 'BINARY_BACKUP_JOB' each time it
# runs, will create multiple copies
# You may wish to clean up after them manually, especially in a
# real customer environment. Study the tests and/or run them individually
# to learn more about what they create, so that you can find and delete it
# yourself. In a dev, PoC, playpen or classroom environment, the cleanup
# might be optional, as the created objects will not interfere with other
# content or work.
#
# Change History
#
# 01Jun2018 Initial version after refactoring tools
# 18oct2018 updated gerrulid test because -o changed to -u
#
#
# Copyright 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
echo "Return all the rest calls that can be made to the folders endpoint"
./callrestapi.py -e /folders -m get
echo
echo "Return the json for all the folders/folders"
./callrestapi.py -e /folders/folders -m get
echo
echo "Return simple text for all the folders/folders"
./callrestapi.py -e /folders/folders -m get -o simple
echo
echo "Rest calls often limit the results returned the text output will tell you returned and total available items"
echo "in this call set a limit above the total items to see everything"
./callrestapi.py -e /folders/folders?limit=500 -m get -o simple
echo
echo "Return the json for all the identities"
./callrestapi.py -e /identities/identities -m get
echo
echo "Return the json for all the identities output to a file"
./callrestapi.py -e /identities/identities -m get > /tmp/identities.json
echo
echo "Contents of /tmp/identities.json:"
cat /tmp/identities.json
echo "End of contents of /tmp/identities.json"
echo
echo "Deleting /tmp/identities.json"
rm /tmp/identities.json
echo "Demonstrating that /tmp/identities.json has been deleted - list it, ls should say no such file or directory:"
ls -al /tmp/identities.json
echo
echo "Refresh the identities cache"
./callrestapi.py -e /identities/userCount -m get
./callrestapi.py -e /identities/cache/refreshes -m post
echo
echo "Pass the folder path and return the folder id and uri"
./getfolderid.py -f /gelcontent
echo
echo "Delete a folder based on its path - we don't want to delete a real folder, so try (and fail) to delete one which does not exist"
./deletefolder.py -f /this_folder_does_not_exist
echo
echo "Delete a folder and its content - we don't want to delete a real folder, so try (and fail) to delete one which does not exist"
./deletefolderandcontent.py -f /this_folder_does_not_exist
echo
echo "Return a set of configuration properties"
./getconfigurationproperties.py -c sas.identities.providers.ldap.user
echo
echo "Create a domain using createdomain"
./createdomain.py -t password -d test -u sasadm -p lnxsas -g "SASAdministrators,HRs,Sales"
echo
echo "Create a binary backup job"
./createbinarybackup.py
echo
echo "Get a rule ID"
#Get /Public folder ID
./getfolderid.py --folderpath /Public > /tmp/folderid.txt
id=$(grep "Id " /tmp/folderid.txt | tr -s ' ' | cut -f3 -d " ")
echo "The Public folder ID is" $id
./getruleid.py -u /folders/folders/$id/** -p authenticatedUsers
echo
echo "Move all content from one folder to another folder (or in this case, the same folder)"
./movecontent.py -s /gelcontent/GELCorp/Shared/Reports -t /gelcontent/GELCorp/Shared/Reports -q
echo
echo "Test folder access"
./testfolderaccess.py -f '/gelcontent/GELCorp' -n gelcorp -t group -m read -s grant
echo
echo "Display all sasadministrator rules"
./listrules.py --p SASadministrators -o simple
echo
echo "Display all rules that contain SASVisual in the URI"
./listrules.py -u SASVisual -o simple
echo
#!/usr/bin/sh
#
# unittestsadm33.sh
# June 2018
#
# Calls each of the pyviyatools at least once, as a simple unit/integration test
#
# Some tests are provided with example folder paths which are not likely to
# exist in your deployment. However, most tests are not dependent on any
# custom content in the deployment, and will run well on any deployment.
#
# Some tests intentionally do things which do not work, e.g. delete a folder
# which does not exist. The error message returned by the tool called is
# considered sufficient to demonstrate that it has in fact been called, and is
# working as intended. If you like, you could create content for these tests
# to act on, e.g. create a folder called "/this_folder_does_not_exist", and
# allow one of the tests below delete it.
#
# The following tests create new content, and do not clean up after themselves:
# 1. "Create a domain using createdomain"
# - creates or replaces domain named 'test', does not create multiple
# copies
# 2. "Create a binary backup job"
# - creates a new scheduled job named 'BINARY_BACKUP_JOB' each time it
# runs, will create multiple copies
# You may wish to clean up after them manually, especially in a
# real customer environment. Study the tests and/or run them individually
# to learn more about what they create, so that you can find and delete it
# yourself. In a dev, PoC, playpen or classroom environment, the cleanup
# might be optional, as the created objects will not interfere with other
# content or work.
#
# Change History
#
# 01Jun2018 Initial version after refactoring tools
# 18oct2018 updated gerrulid test because -o changed to -u
#
#
# Copyright 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
echo "Return all the rest calls that can be made to the folders endpoint"
./callrestapi.py -e /folders -m get
echo
echo "Return the json for all the folders/folders"
./callrestapi.py -e /folders/folders -m get
echo
echo "Return simple text for all the folders/folders"
./callrestapi.py -e /folders/folders -m get -o simple
echo
echo "Rest calls often limit the results returned the text output will tell you returned and total available items"
echo "in this call set a limit above the total items to see everything"
./callrestapi.py -e /folders/folders?limit=500 -m get -o simple
echo
echo "Return the json for all the identities"
./callrestapi.py -e /identities/identities -m get
echo
echo "Return the json for all the identities output to a file"
./callrestapi.py -e /identities/identities -m get > /tmp/identities.json
echo
echo "Contents of /tmp/identities.json:"
cat /tmp/identities.json
echo "End of contents of /tmp/identities.json"
echo
echo "Deleting /tmp/identities.json"
rm /tmp/identities.json
echo "Demonstrating that /tmp/identities.json has been deleted - list it, ls should say no such file or directory:"
ls -al /tmp/identities.json
echo
echo "Refresh the identities cache"
./callrestapi.py -e /identities/userCount -m get
./callrestapi.py -e /identities/cache/refreshes -m post
echo
echo "Pass the folder path and return the folder id and uri"
./getfolderid.py -f /gelcontent
echo
echo "Delete a folder based on its path - we don't want to delete a real folder, so try (and fail) to delete one which does not exist"
./deletefolder.py -f /this_folder_does_not_exist
echo
echo "Delete a folder and its content - we don't want to delete a real folder, so try (and fail) to delete one which does not exist"
./deletefolderandcontent.py -f /this_folder_does_not_exist
echo
echo "Return a set of configuration properties"
./getconfigurationproperties.py -c sas.identities.providers.ldap.user
echo
echo "Create a domain using createdomain"
./createdomain.py -t password -d test -u sasadm -p lnxsas -g "SASAdministrators,HRs,Sales"
echo
echo "Create a binary backup job"
./createbinarybackup.py
echo
echo "Get a rule ID"
#Get /Public folder ID
./getfolderid.py --folderpath /Public > /tmp/folderid.txt
id=$(grep "Id " /tmp/folderid.txt | tr -s ' ' | cut -f3 -d " ")
echo "The Public folder ID is" $id
./getruleid.py -u /folders/folders/$id/** -p authenticatedUsers
echo
echo "Move all content from one folder to another folder (or in this case, the same folder)"
./movecontent.py -s /gelcontent/GELCorp/Shared/Reports -t /gelcontent/GELCorp/Shared/Reports -q
echo
echo "Test folder access"
./testfolderaccess.py -f '/gelcontent/GELCorp' -n gelcorp -t group -m read -s grant
echo
echo "Display all sasadministrator rules"
./listrules.py --p SASadministrators -o simple
echo
echo "Display all rules that contain SASVisual in the URI"
./listrules.py -u SASVisual -o simple
echo

362
unittestsadm34.sh

@ -1,181 +1,181 @@
#!/usr/bin/sh
#
# unittestsadm33.sh
# December 2018
#
# Calls each of the pyviyatools at least once, as a simple unit/integration test
#
# Some tests are provided with example folder paths which are not likely to
# exist in your deployment. However, most tests are not dependent on any
# custom content in the deployment, and will run well on any deployment.
#
# Some tests intentionally do things which do not work, e.g. delete a folder
# which does not exist. The error message returned by the tool called is
# considered sufficient to demonstrate that it has in fact been called, and is
# working as intended. If you like, you could create content for these tests
# to act on, e.g. create a folder called "/this_folder_does_not_exist", and
# allow one of the tests below delete it.
#
# The following tests create new content, and do not clean up after themselves:
# 1. "Create a domain using createdomain"
# - creates or replaces domain named 'test', does not create multiple
# copies
# 2. "Create a binary backup job"
# - creates a new scheduled job named 'BINARY_BACKUP_JOB' each time it
# runs, will create multiple copies
# You may wish to clean up after them manually, especially in a
# real customer environment. Study the tests and/or run them individually
# to learn more about what they create, so that you can find and delete it
# yourself. In a dev, PoC, playpen or classroom environment, the cleanup
# might be optional, as the created objects will not interfere with other
# content or work.
#
# Change History
#
# 01Jun2018 Initial version after refactoring tools
# 18Oct2018 updated gerrulid test because -o changed to -u
# 03Dec2018 Added tests for explainaccess.py
#
#
# Copyright 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
echo "Return all the rest calls that can be made to the folders endpoint"
./callrestapi.py -e /folders -m get
echo
echo "Return the json for all the folders/folders"
./callrestapi.py -e /folders/folders -m get
echo
echo "Return simple text for all the folders/folders"
./callrestapi.py -e /folders/folders -m get -o simple
echo
echo "Rest calls often limit the results returned the text output will tell you returned and total available items"
echo "in this call set a limit above the total items to see everything"
./callrestapi.py -e /folders/folders?limit=500 -m get -o simple
echo
echo "Return the json for all the identities"
./callrestapi.py -e /identities/identities -m get
echo
echo "Return the json for all the identities output to a file"
./callrestapi.py -e /identities/identities -m get > /tmp/identities.json
echo
echo "Contents of /tmp/identities.json:"
cat /tmp/identities.json
echo "End of contents of /tmp/identities.json"
echo
echo "Deleting /tmp/identities.json"
rm /tmp/identities.json
echo "Demonstrating that /tmp/identities.json has been deleted - list it, ls should say no such file or directory:"
ls -al /tmp/identities.json
echo
echo "Refresh the identities cache"
./callrestapi.py -e /identities/userCount -m get
./callrestapi.py -e /identities/cache/refreshes -m post
echo
echo "Pass the folder path and return the folder id and uri"
./getfolderid.py -f /gelcontent
echo
echo "Delete a folder based on its path - we don't want to delete a real folder, so try (and fail) to delete one which does not exist"
./deletefolder.py -f /this_folder_does_not_exist
echo
echo "Delete a folder and its content - we don't want to delete a real folder, so try (and fail) to delete one which does not exist"
./deletefolderandcontent.py -f /this_folder_does_not_exist
echo
echo "Return a set of configuration properties"
./getconfigurationproperties.py -c sas.identities.providers.ldap.user
echo
echo "Create a domain using createdomain"
./createdomain.py -t password -d test -u sasadm -p lnxsas -g "SASAdministrators,HR,Sales"
echo
# Commented out for Viya 3.4 version: this tool is only intended for use with Viya 3.3
#echo "Create a binary backup job"
#./createbinarybackup.py
#echo
echo "Get a rule ID"
#Get /Public folder ID
./getfolderid.py --folderpath /Public > /tmp/folderid.txt
id=$(grep "Id " /tmp/folderid.txt | tr -s ' ' | cut -f3 -d " ")
echo "The Public folder ID is" $id
./getruleid.py -u /folders/folders/$id/** -p authenticatedUsers
echo
echo "Move all content from one folder to another folder (or in this case, the same folder)"
./movecontent.py -s /gelcontent/GELCorp/Shared/Reports -t /gelcontent/GELCorp/Shared/Reports -q
echo
echo "Test folder access"
./testfolderaccess.py -f '/gelcontent/GELCorp' -n gelcorp -t group -m read -s grant
echo
echo "Display all sasadministrator rules"
./listrules.py --p SASadministrators -o simple
echo
echo "Display all rules that contain SASVisual in the URI"
./listrules.py -u SASVisual -o simple
echo
echo "Create folders from a CSV file"
./createfolders.py -h
echo
echo Update the theme for the user sasadm
./updatepreferences.py -t user -tn sasadm -pi OpenUI.Theme.Default -pv sas_hcb
echo
echo "Explain permissions for the folder /gelcontent/GELCorp, with no header row."
./explainaccess.py -f /gelcontent/GELCorp
echo
echo "Explain permissions for the folder /gelcontent/GELCorp, with no header row, for Heather."
./explainaccess.py -f /gelcontent/GELCorp -n Heather -t user
echo
echo "Explain permissions for the folder /gelcontent/GELCorp, with a header row, and include the folder path."
./explainaccess.py -f /gelcontent/GELCorp --header -p
echo
echo "Explain permissions for the folder /gelcontent/GELCorp, showing only rows with at least one direct permission."
./explainaccess.py -f /gelcontent/GELCorp --direct_only
echo
echo "Explain permissions for several application capabilities. Notice that there are no conveyed permissions in the results"
./explainaccess.py -u /SASEnvironmentManager/dashboard --header -p -l read update delete secure add remove create
./explainaccess.py -u /SASDataStudio/** -p -l read update delete secure add remove create
./explainaccess.py -u /SASDataExplorer/** -p -l read update delete secure add remove create
./explainaccess.py -u /SASVisualAnalytics_capabilities/buildAnalyticalModel -p -l read update delete secure add remove create
echo
echo "Explain the permissions for a folder expressed as a URI. This works with any kind of object, not just folders"
echo "Uses the default permissions list. Specify -c true to include conveyed permissions, as they are not included by default when you use the -u URI parameter."
#Get /gelcontent/GELCorp folder ID
./getfolderid.py --folderpath /gelcontent/GELCorp > /tmp/folderid.txt
id=$(grep "Id " /tmp/folderid.txt | tr -s ' ' | cut -f3 -d " ")
./explainaccess.py -u /folders/folders/$id --header -p -c true
echo
#!/usr/bin/sh
#
# unittestsadm33.sh
# December 2018
#
# Calls each of the pyviyatools at least once, as a simple unit/integration test
#
# Some tests are provided with example folder paths which are not likely to
# exist in your deployment. However, most tests are not dependent on any
# custom content in the deployment, and will run well on any deployment.
#
# Some tests intentionally do things which do not work, e.g. delete a folder
# which does not exist. The error message returned by the tool called is
# considered sufficient to demonstrate that it has in fact been called, and is
# working as intended. If you like, you could create content for these tests
# to act on, e.g. create a folder called "/this_folder_does_not_exist", and
# allow one of the tests below delete it.
#
# The following tests create new content, and do not clean up after themselves:
# 1. "Create a domain using createdomain"
# - creates or replaces domain named 'test', does not create multiple
# copies
# 2. "Create a binary backup job"
# - creates a new scheduled job named 'BINARY_BACKUP_JOB' each time it
# runs, will create multiple copies
# You may wish to clean up after them manually, especially in a
# real customer environment. Study the tests and/or run them individually
# to learn more about what they create, so that you can find and delete it
# yourself. In a dev, PoC, playpen or classroom environment, the cleanup
# might be optional, as the created objects will not interfere with other
# content or work.
#
# Change History
#
# 01Jun2018 Initial version after refactoring tools
# 18Oct2018 updated gerrulid test because -o changed to -u
# 03Dec2018 Added tests for explainaccess.py
#
#
# Copyright 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
echo "Return all the rest calls that can be made to the folders endpoint"
./callrestapi.py -e /folders -m get
echo
echo "Return the json for all the folders/folders"
./callrestapi.py -e /folders/folders -m get
echo
echo "Return simple text for all the folders/folders"
./callrestapi.py -e /folders/folders -m get -o simple
echo
echo "Rest calls often limit the results returned the text output will tell you returned and total available items"
echo "in this call set a limit above the total items to see everything"
./callrestapi.py -e /folders/folders?limit=500 -m get -o simple
echo
echo "Return the json for all the identities"
./callrestapi.py -e /identities/identities -m get
echo
echo "Return the json for all the identities output to a file"
./callrestapi.py -e /identities/identities -m get > /tmp/identities.json
echo
echo "Contents of /tmp/identities.json:"
cat /tmp/identities.json
echo "End of contents of /tmp/identities.json"
echo
echo "Deleting /tmp/identities.json"
rm /tmp/identities.json
echo "Demonstrating that /tmp/identities.json has been deleted - list it, ls should say no such file or directory:"
ls -al /tmp/identities.json
echo
echo "Refresh the identities cache"
./callrestapi.py -e /identities/userCount -m get
./callrestapi.py -e /identities/cache/refreshes -m post
echo
echo "Pass the folder path and return the folder id and uri"
./getfolderid.py -f /gelcontent
echo
echo "Delete a folder based on its path - we don't want to delete a real folder, so try (and fail) to delete one which does not exist"
./deletefolder.py -f /this_folder_does_not_exist
echo
echo "Delete a folder and its content - we don't want to delete a real folder, so try (and fail) to delete one which does not exist"
./deletefolderandcontent.py -f /this_folder_does_not_exist
echo
echo "Return a set of configuration properties"
./getconfigurationproperties.py -c sas.identities.providers.ldap.user
echo
echo "Create a domain using createdomain"
./createdomain.py -t password -d test -u sasadm -p lnxsas -g "SASAdministrators,HR,Sales"
echo
# Commented out for Viya 3.4 version: this tool is only intended for use with Viya 3.3
#echo "Create a binary backup job"
#./createbinarybackup.py
#echo
echo "Get a rule ID"
#Get /Public folder ID
./getfolderid.py --folderpath /Public > /tmp/folderid.txt
id=$(grep "Id " /tmp/folderid.txt | tr -s ' ' | cut -f3 -d " ")
echo "The Public folder ID is" $id
./getruleid.py -u /folders/folders/$id/** -p authenticatedUsers
echo
echo "Move all content from one folder to another folder (or in this case, the same folder)"
./movecontent.py -s /gelcontent/GELCorp/Shared/Reports -t /gelcontent/GELCorp/Shared/Reports -q
echo
echo "Test folder access"
./testfolderaccess.py -f '/gelcontent/GELCorp' -n gelcorp -t group -m read -s grant
echo
echo "Display all sasadministrator rules"
./listrules.py --p SASadministrators -o simple
echo
echo "Display all rules that contain SASVisual in the URI"
./listrules.py -u SASVisual -o simple
echo
echo "Create folders from a CSV file"
./createfolders.py -h
echo
echo Update the theme for the user sasadm
./updatepreferences.py -t user -tn sasadm -pi OpenUI.Theme.Default -pv sas_hcb
echo
echo "Explain permissions for the folder /gelcontent/GELCorp, with no header row."
./explainaccess.py -f /gelcontent/GELCorp
echo
echo "Explain permissions for the folder /gelcontent/GELCorp, with no header row, for Heather."
./explainaccess.py -f /gelcontent/GELCorp -n Heather -t user
echo
echo "Explain permissions for the folder /gelcontent/GELCorp, with a header row, and include the folder path."
./explainaccess.py -f /gelcontent/GELCorp --header -p
echo
echo "Explain permissions for the folder /gelcontent/GELCorp, showing only rows with at least one direct permission."
./explainaccess.py -f /gelcontent/GELCorp --direct_only
echo
echo "Explain permissions for several application capabilities. Notice that there are no conveyed permissions in the results"
./explainaccess.py -u /SASEnvironmentManager/dashboard --header -p -l read update delete secure add remove create
./explainaccess.py -u /SASDataStudio/** -p -l read update delete secure add remove create
./explainaccess.py -u /SASDataExplorer/** -p -l read update delete secure add remove create
./explainaccess.py -u /SASVisualAnalytics_capabilities/buildAnalyticalModel -p -l read update delete secure add remove create
echo
echo "Explain the permissions for a folder expressed as a URI. This works with any kind of object, not just folders"
echo "Uses the default permissions list. Specify -c true to include conveyed permissions, as they are not included by default when you use the -u URI parameter."
#Get /gelcontent/GELCorp folder ID
./getfolderid.py --folderpath /gelcontent/GELCorp > /tmp/folderid.txt
id=$(grep "Id " /tmp/folderid.txt | tr -s ' ' | cut -f3 -d " ")
./explainaccess.py -u /folders/folders/$id --header -p -c true
echo

216
updatedomain.py

@ -1,108 +1,108 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# updatedomain.py
# November 2018
#
# update a viya domain to add credentials from a csv
# the domain must exist
#
# Change History
#
# 27JAN2017 Comments added
# 03DEC2018 Strip spaces from the end of users and groups
#
# csv file format
# no header row
# column1 is userid
# column2 is password
# column3 is identity
# column4 is identity type (user or group)
# For example:
# myuserid,mypass,Sales,group
# acct1,pw1,Admin,user
# etc
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
# Update a domain
import csv
import base64
import argparse
from sharedfunctions import callrestapi, file_accessible
parser = argparse.ArgumentParser(description="Update a Viya Domain to add credentials from a csv file.")
parser.add_argument("-d","--domain", help="Existing Domain.",required=True)
parser.add_argument("-f","--file", help="A csv file containing groups and userids.",required=True)
args = parser.parse_args()
domain_name=args.domain
file=args.file
# check that domain exists
reqval="/credentials/domains/"+domain_name
reqtype="get"
#if domain does not exist call restapi will exit and no additional code is run
domainexist=callrestapi(reqval,reqtype)
type=domainexist['type']
# read the csv file to create json
check=file_accessible(file,'r')
# file can be read
if check:
with open(file, 'rt') as f:
filecontents = csv.reader(f)
for row in filecontents:
#print(row)
userid=row[0]
pwval=row[1]
ident=row[2].rstrip()
identtype=row[3].rstrip()
if pwval: cred=base64.b64encode(pwval.encode("utf-8")).decode("utf-8")
if identtype=="group": end_ident="groups"
if identtype=="user": end_ident="users"
reqval="/credentials/domains/"+domain_name+"/"+end_ident+"/"+ident
reqtype="put"
data = {}
data['domainId'] = domain_name
data['domainType'] = type
data['identityId'] = ident
data['identityType'] = identtype
data['properties']={"userId": userid}
if pwval:
data['secrets']={"password": cred}
#print(reqval)
#print(data)
# make the rest call
callrestapi(reqval,reqtype,data=data)
else:
print("ERROR: cannot read "+file)
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# updatedomain.py
# November 2018
#
# update a viya domain to add credentials from a csv
# the domain must exist
#
# Change History
#
# 27JAN2017 Comments added
# 03DEC2018 Strip spaces from the end of users and groups
#
# csv file format
# no header row
# column1 is userid
# column2 is password
# column3 is identity
# column4 is identity type (user or group)
# For example:
# myuserid,mypass,Sales,group
# acct1,pw1,Admin,user
# etc
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
# Update a domain
import csv
import base64
import argparse
from sharedfunctions import callrestapi, file_accessible
parser = argparse.ArgumentParser(description="Update a Viya Domain to add credentials from a csv file.")
parser.add_argument("-d","--domain", help="Existing Domain.",required=True)
parser.add_argument("-f","--file", help="A csv file containing groups and userids.",required=True)
args = parser.parse_args()
domain_name=args.domain
file=args.file
# check that domain exists
reqval="/credentials/domains/"+domain_name
reqtype="get"
#if domain does not exist call restapi will exit and no additional code is run
domainexist=callrestapi(reqval,reqtype)
type=domainexist['type']
# read the csv file to create json
check=file_accessible(file,'r')
# file can be read
if check:
with open(file, 'rt') as f:
filecontents = csv.reader(f)
for row in filecontents:
#print(row)
userid=row[0]
pwval=row[1]
ident=row[2].rstrip()
identtype=row[3].rstrip()
if pwval: cred=base64.b64encode(pwval.encode("utf-8")).decode("utf-8")
if identtype=="group": end_ident="groups"
if identtype=="user": end_ident="users"
reqval="/credentials/domains/"+domain_name+"/"+end_ident+"/"+ident
reqtype="put"
data = {}
data['domainId'] = domain_name
data['domainType'] = type
data['identityId'] = ident
data['identityType'] = identtype
data['properties']={"userId": userid}
if pwval:
data['secrets']={"password": cred}
#print(reqval)
#print(data)
# make the rest call
callrestapi(reqval,reqtype,data=data)
else:
print("ERROR: cannot read "+file)

196
updatepreferences.py

@ -1,99 +1,99 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# updatepreferences.py
# October 2018
#
# Update user preferences for a single user of a group of users
#
# Change History
#
# 30OCT2018 first version
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
####################################################################
#### COMMAND LINE EXAMPLE ####
####################################################################
#### ./updatepreferences.py - ####
#### -t user ####
#### -tn myUser ####
#### -pi VA.geo.drivedistance.unit ####
#### -pv kilometers ####
####################################################################
#### POSSIBLE VALUES ####
####################################################################
#### sas.welcome.suppress = true/false ####
#### sas.drive.show.pinned = true/false ####
#### VA.geo.drivedistance.unit = kilometers/miles ####
#### OpenUI.Theme.Default = sas_corporate/sas_inspire/sas_hcb ####
####################################################################
import argparse
from sharedfunctions import callrestapi
parser = argparse.ArgumentParser(description="Update user preferences for a user or a group of users")
parser.add_argument("-t", "--target", help="Type the target of the update: user or group", required=True, choices=['user', 'group'])
parser.add_argument("-tn", "--targetname", help="ID of the user or group to which the update applies.", required=True)
parser.add_argument("-pi", "--preferenceid", help="ID of the preference to be updated", required=True)
parser.add_argument("-pv", "--preferencevalue", help="Value to be set for the preference", required=True)
args = parser.parse_args()
target = args.target
targetName = args.targetname
preferenceID = args.preferenceid
preferenceValue = args.preferencevalue
json= {"application": "SAS Visual Analytics", "version": 1,"id": preferenceID ,"value": preferenceValue}
# Function to update preference of a specific user
if target == 'user' :
userID=targetName
reqtype='get'
reqval="/identities/users/"+userID
userexist=callrestapi(reqval,reqtype)
reqtype="put"
reqval="/preferences/preferences/"+ userID +"/" + preferenceID
result=callrestapi(reqval,reqtype,data=json)
print("Updating Preference "+reqval+" = "+preferenceValue)
else: # Execute actual code to update the preference for a user or a group
reqtype='get'
reqval='/identities/groups/'+ targetName +'/members?limit=1000'
resultdata=callrestapi(reqval,reqtype)
reqtype="put"
if 'items' in resultdata:
returned_items=len(resultdata['items'])
for i in range(0,returned_items):
id=resultdata['items'][i]['id']
type=resultdata['items'][i]['type']
if type=="user":
reqval="/preferences/preferences/"+ id +"/" + preferenceID
result=callrestapi(reqval, reqtype,data=json,stoponerror=0)
print(result)
print("Updating Preference "+reqval+" = "+preferenceValue)
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# updatepreferences.py
# October 2018
#
# Update user preferences for a single user of a group of users
#
# Change History
#
# 30OCT2018 first version
#
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
####################################################################
#### COMMAND LINE EXAMPLE ####
####################################################################
#### ./updatepreferences.py - ####
#### -t user ####
#### -tn myUser ####
#### -pi VA.geo.drivedistance.unit ####
#### -pv kilometers ####
####################################################################
#### POSSIBLE VALUES ####
####################################################################
#### sas.welcome.suppress = true/false ####
#### sas.drive.show.pinned = true/false ####
#### VA.geo.drivedistance.unit = kilometers/miles ####
#### OpenUI.Theme.Default = sas_corporate/sas_inspire/sas_hcb ####
####################################################################
import argparse
from sharedfunctions import callrestapi
parser = argparse.ArgumentParser(description="Update user preferences for a user or a group of users")
parser.add_argument("-t", "--target", help="Type the target of the update: user or group", required=True, choices=['user', 'group'])
parser.add_argument("-tn", "--targetname", help="ID of the user or group to which the update applies.", required=True)
parser.add_argument("-pi", "--preferenceid", help="ID of the preference to be updated", required=True)
parser.add_argument("-pv", "--preferencevalue", help="Value to be set for the preference", required=True)
args = parser.parse_args()
target = args.target
targetName = args.targetname
preferenceID = args.preferenceid
preferenceValue = args.preferencevalue
json= {"application": "SAS Visual Analytics", "version": 1,"id": preferenceID ,"value": preferenceValue}
# Function to update preference of a specific user
if target == 'user' :
userID=targetName
reqtype='get'
reqval="/identities/users/"+userID
userexist=callrestapi(reqval,reqtype)
reqtype="put"
reqval="/preferences/preferences/"+ userID +"/" + preferenceID
result=callrestapi(reqval,reqtype,data=json)
print("Updating Preference "+reqval+" = "+preferenceValue)
else: # Execute actual code to update the preference for a user or a group
reqtype='get'
reqval='/identities/groups/'+ targetName +'/members?limit=1000'
resultdata=callrestapi(reqval,reqtype)
reqtype="put"
if 'items' in resultdata:
returned_items=len(resultdata['items'])
for i in range(0,returned_items):
id=resultdata['items'][i]['id']
type=resultdata['items'][i]['type']
if type=="user":
reqval="/preferences/preferences/"+ id +"/" + preferenceID
result=callrestapi(reqval, reqtype,data=json,stoponerror=0)
print(result)
print("Updating Preference "+reqval+" = "+preferenceValue)
else: print("Cannot set preferences for a group "+id )
Loading…
Cancel
Save