committed by
GitHub
22 changed files with 2630 additions and 0 deletions
@ -0,0 +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> |
|||
|
|||
|
|||
|
|||
using your real name (sorry, no pseudonyms or anonymous contributions.) |
|||
@ -0,0 +1,32 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# call_rest_api.py |
|||
# |
|||
# Includes callrestapi.py, providing backward compatibility with previous version of this tool |
|||
|
|||
# |
|||
# 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 os |
|||
#print(os.path.dirname(os.path.realpath(__file__))) |
|||
scriptdir=os.path.dirname(os.path.realpath(__file__)) |
|||
|
|||
def include(filename): |
|||
if os.path.exists(filename): |
|||
execfile(filename) |
|||
include(os.path.join(scriptdir,'callrestapi.py')) |
|||
|
|||
@ -0,0 +1,76 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# callrestapi.py |
|||
# December 2017 |
|||
# |
|||
# Based on the items passed in the utility calls the rest api and return results |
|||
# |
|||
# Change History |
|||
# |
|||
# 27JAN2017 Comments added |
|||
# 29JAN2017 Added choices to validate method input |
|||
# 31JAN2017 Added contenttype parameters |
|||
# 02FEB2018 Added simple text print flag |
|||
# 01JUN2018 Renamed from call_rest_api.py to callrestapi.py |
|||
# 08JUN2018 Print json instead of pprint of easier result parsing |
|||
# 01JUN2018 Renamed from call_rest_api.py to callrestapi.py |
|||
# 08JUN2018 Print json instead of pprint of easier result parsing |
|||
# 08OCT2018 make printed json pretty |
|||
# 26OCT2018 call print function |
|||
|
|||
|
|||
# |
|||
# 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,getinputjson,printresult |
|||
|
|||
# get command line parameters |
|||
|
|||
parser = argparse.ArgumentParser(description="Call the Viya REST API") |
|||
parser.add_argument("-e","--endpoint", help="Enter the REST endpoint e.g. /folders/folders ",required='True') |
|||
parser.add_argument("-m","--method", help="Enter the REST method.",default="get",required='True',choices=['get','put','post','delete']) |
|||
parser.add_argument("-i","--inputfile",help="Enter the full path to an input json file",default=None) |
|||
parser.add_argument("-a","--accepttype",help="Enter REST Content Type you want returned e.g application/vnd.sas.identity.basic+json",default="application/json") |
|||
parser.add_argument("-c","--contenttype",help="Enter REST Content Type for POST e.g application/vnd.sas.identity.basic+json",default="application/json") |
|||
parser.add_argument("-o","--output", help="Output Style", choices=['csv','json','simple'],default='json') |
|||
parser.add_argument("-t","--text", help="Display Simple Text Results.", action='store_true') |
|||
|
|||
args = parser.parse_args() |
|||
|
|||
reqval=args.endpoint |
|||
reqtype=args.method |
|||
reqfile=args.inputfile |
|||
reqcontent=args.contenttype |
|||
reqaccept=args.accepttype |
|||
simpletext=args.text |
|||
output_style=args.output |
|||
|
|||
# keep for backward compatibility |
|||
if simpletext: output_style='simple' |
|||
|
|||
# use the callrestapi function to make a call to the endpoint |
|||
# call passing json or not |
|||
if reqfile != None: |
|||
inputdata=getinputjson(reqfile) |
|||
result=callrestapi(reqval,reqtype,reqaccept,reqcontent,data=inputdata) |
|||
else: |
|||
result=callrestapi(reqval,reqtype,reqaccept,reqcontent) |
|||
|
|||
#print the result |
|||
printresult(result,output_style) |
|||
@ -0,0 +1,131 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# createbinarybackup.py |
|||
# February 2018 |
|||
# |
|||
# Usage: |
|||
# python createbinarybackup.py [-q] [-d] |
|||
# |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION |
|||
clidir='/opt/sas/viya/home/bin/' |
|||
debug=False |
|||
defaultBackupScheduleName="DEFAULT_BACKUP_SCHEDULE" |
|||
newScheduleName="BINARY_BACKUP_SCHEDULE" |
|||
newScheduleDesc="JobRequest to execute a binary backup" |
|||
jobDefinitionURIStem="/jobDefinitions/definitions/" |
|||
newScheduleContentType="application/vnd.sas.backup.request+json" # For a single-tenant deployment |
|||
#newScheduleContentType="application/vnd.sas.backup.deployment.request+json" # For a multi-tenant deployment |
|||
|
|||
# Import Python modules |
|||
|
|||
import argparse |
|||
import json |
|||
import sys |
|||
|
|||
from sharedfunctions import 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("-t","--principaltype", help="Enter the type of principal to test: user or group.",required='True',choices=['user','group']) |
|||
parser.add_argument("-q","--quiet", action='store_true') |
|||
parser.add_argument("-d","--debug", action='store_true') |
|||
args = parser.parse_args() |
|||
#principaltype=args.principaltype |
|||
quiet=args.quiet |
|||
debug=args.debug |
|||
|
|||
# STEP 1 of 4: Get the jobDefinition of the existing DEFAULT_BACKUP_SCHEDULE |
|||
|
|||
endpoint='/jobDefinitions/definitions?limit=20&filter=in(name,"'+defaultBackupScheduleName+'")' |
|||
method='get' |
|||
accept='application/json' |
|||
|
|||
jobDefinition_json=callrestapi(endpoint,method,accept) |
|||
if debug: |
|||
print('jobDefinition_json:') |
|||
print(jobDefinition_json) |
|||
|
|||
jobDefinitions=jobDefinition_json['items'] |
|||
id_found=False |
|||
jobDefinitionId='' |
|||
for jobDefinition in jobDefinitions: |
|||
if jobDefinition['name']: |
|||
if(jobDefinition['name']==defaultBackupScheduleName): |
|||
jobDefinitionId=jobDefinition['id'] |
|||
print('Id: '+jobDefinitionId) |
|||
id_found=True |
|||
|
|||
if not id_found: |
|||
raise Exception('Unable to determine Id for '+defaultBackupScheduleName+'.') |
|||
|
|||
# STEP 2 of 4: Create a jobExecution request |
|||
|
|||
endpoint='/jobExecution/jobRequests' |
|||
method='post' |
|||
accept='application/vnd.sas.job.execution.job.request+json' |
|||
content='application/vnd.sas.job.execution.job.request+json' |
|||
inputdata={ |
|||
"name": newScheduleName, |
|||
"description": newScheduleDesc, |
|||
"jobDefinitionUri": jobDefinitionURIStem+jobDefinitionId, |
|||
"arguments": { |
|||
"contentType": newScheduleContentType, |
|||
"backupType": "binary" |
|||
} |
|||
} |
|||
|
|||
jobExecutionRequest_json=callrestapi(endpoint,method,accept,content,inputdata) |
|||
if debug: |
|||
print('jobExecutionRequest_json:') |
|||
print(jobExecutionRequest_json) |
|||
|
|||
# STEP 3 of 4: Get the href to submit the job from the create jobExecution response |
|||
|
|||
links=jobExecutionRequest_json['links'] |
|||
href_found=False |
|||
submitJobHref='' |
|||
for link in links: |
|||
if link['rel']: |
|||
if(link['rel']=="submitJob"): |
|||
submitJobHref=link['href'] |
|||
print('Href: '+submitJobHref) |
|||
href_found=True |
|||
|
|||
if not href_found: |
|||
raise Exception('Unable to find the href for the submitJob link.') |
|||
|
|||
# STEP 4 of 4: Submit the jobExecution request |
|||
|
|||
endpoint=submitJobHref |
|||
method='post' |
|||
accept='application/vnd.sas.job.execution.job+json' |
|||
|
|||
submitJob_json=callrestapi(endpoint,method,accept) |
|||
#if debug: |
|||
print('submitJob_json:') |
|||
print(submitJob_json) |
|||
@ -0,0 +1,97 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# createdomain.py |
|||
# December 2017 |
|||
# |
|||
# create a viya domain |
|||
# |
|||
# Change History |
|||
# |
|||
# 27JAN2017 Comments added |
|||
# 27JAN2017 Added the ability to create connection domains |
|||
# 29JAN2017 Added choices to validate type of domain |
|||
# 29SEP2018 make group list comma seperated |
|||
# |
|||
|
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
# Create a domain |
|||
|
|||
import base64 |
|||
import argparse |
|||
|
|||
from sharedfunctions import callrestapi |
|||
|
|||
parser = argparse.ArgumentParser(description="Create a Viya Domain") |
|||
parser.add_argument("-d","--domain", help="Enter the domain name.",required=True) |
|||
parser.add_argument("-u","--user", help="User ID for the domain.",required=True) |
|||
parser.add_argument("-p","--password", help="Password for the userid.",required=False) |
|||
parser.add_argument("-g","--groups", help="A list of groups to add to the domain. Groupid comma seperated",required=True) |
|||
parser.add_argument("-c","--desc", help="Description of the domain.",required=False) |
|||
parser.add_argument("-t","--type", help="Type of the domain: password or connection (passwordless).",required=True, choices=['password','connection']) |
|||
|
|||
args = parser.parse_args() |
|||
|
|||
domain_name=args.domain |
|||
userid=args.user |
|||
pwval=args.password |
|||
groups=args.groups |
|||
desc=args.desc |
|||
type=args.type |
|||
|
|||
# create a python list with the groups |
|||
grouplist=groups.split(",") |
|||
|
|||
# encode the password |
|||
if pwval: |
|||
cred=base64.b64encode(pwval.encode("utf-8")).decode("utf-8") |
|||
|
|||
# build the rest call |
|||
reqval="/credentials/domains/"+domain_name |
|||
reqtype="put" |
|||
|
|||
# build the json parameters |
|||
data = {} |
|||
data['id'] = domain_name |
|||
data['description'] = desc |
|||
data['type'] = type |
|||
|
|||
# create the domain |
|||
callrestapi(reqval,reqtype,data=data) |
|||
|
|||
# for each group passed in add their credentials to the domain |
|||
for group_id in grouplist: |
|||
print("Adding "+ group_id + " to domain " + domain_name) |
|||
|
|||
reqval="/credentials/domains/"+domain_name+"/groups/"+group_id |
|||
reqtype="put" |
|||
|
|||
data = {} |
|||
data['domainId'] = domain_name |
|||
data['domainType'] = type |
|||
data['identityId'] = group_id |
|||
data['identityType'] = 'group' |
|||
data['properties']={"userId": userid} |
|||
if pwval: |
|||
data['secrets']={"password": cred} |
|||
|
|||
print(data) |
|||
|
|||
callrestapi(reqval,reqtype,data=data) |
|||
|
|||
|
|||
@ -0,0 +1,93 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# createfolders.py |
|||
# October 2018 |
|||
# |
|||
# |
|||
# Change History |
|||
# |
|||
# sasgnn 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) |
|||
|
|||
@ -0,0 +1,79 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# deletefolder.py |
|||
# December 2017 |
|||
# |
|||
# Pass in a folder path and delete the folder, its sub-folders |
|||
# |
|||
# Change History |
|||
# |
|||
# 27JAN2018 Comments added |
|||
# 03Feb2018 Added quiet mode |
|||
# 03Mar2018 Made prompt comparison case-insensitive |
|||
|
|||
# |
|||
# 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 Python modules |
|||
|
|||
import argparse, sys |
|||
|
|||
from sharedfunctions import getfolderid, callrestapi |
|||
|
|||
# get python version |
|||
version=int(str(sys.version_info[0])) |
|||
|
|||
# get input parameters |
|||
parser = argparse.ArgumentParser(description="Delete a folder and its sub-folders") |
|||
parser.add_argument("-f","--folderpath", help="Enter the path to the viya folder.",required='True') |
|||
parser.add_argument("-q","--quiet", help="Suppress the are you sure prompt.", action='store_true') |
|||
args = parser.parse_args() |
|||
|
|||
print(args.folderpath) |
|||
path_to_folder=args.folderpath |
|||
quietmode=args.quiet |
|||
|
|||
# call getfolderid to get the folder id |
|||
targets=getfolderid(path_to_folder) |
|||
|
|||
|
|||
# if folder is found |
|||
if targets[0] is not None: |
|||
|
|||
uri=targets[1] |
|||
|
|||
# if the user passed in the quiet key do not prompt are you sure |
|||
if quietmode: |
|||
areyousure="Y" |
|||
else: |
|||
|
|||
# deal with python 2 v python 3 prompts |
|||
if version > 2: |
|||
areyousure=input("Are you sure you want to delete the folder and its contents? (Y)") |
|||
else: |
|||
areyousure=raw_input("Are you sure you want to delete the folder and its contents? (Y)") |
|||
|
|||
|
|||
# delete the folder recursively |
|||
if areyousure.upper() =='Y': |
|||
print("Deleting folder= "+ path_to_folder+" "+uri) |
|||
reqval=uri+"?recursive=true" |
|||
reqtype='delete' |
|||
callrestapi(reqval,reqtype) |
|||
print('Folder Deleted.') |
|||
else: |
|||
print("Good thing I asked!") |
|||
@ -0,0 +1,103 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# deletefolderandcontent.py |
|||
# february 2018 |
|||
# |
|||
# Pass in a folder path and delete the folder, its sub-folders and content |
|||
# |
|||
# Change History |
|||
# |
|||
# 27JAN2018 Comments added |
|||
# 03Feb2018 Added quiet mode |
|||
# based on delete folder, but in addition deletes reports. |
|||
|
|||
# |
|||
# 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 Python modules |
|||
|
|||
import argparse, sys |
|||
|
|||
from sharedfunctions import getfolderid, callrestapi |
|||
|
|||
# get python version |
|||
version=int(str(sys.version_info[0])) |
|||
|
|||
# get input parameters |
|||
parser = argparse.ArgumentParser(description="Delete a folder and its sub-folders and contents") |
|||
parser.add_argument("-f","--folderpath", help="Enter the path to the viya folder.",required='True') |
|||
parser.add_argument("-q","--quiet", help="Suppress the are you sure prompt.", action='store_true') |
|||
args = parser.parse_args() |
|||
|
|||
print(args.folderpath) |
|||
path_to_folder=args.folderpath |
|||
quietmode=args.quiet |
|||
|
|||
# call getfolderid to get the folder id |
|||
targets=getfolderid(path_to_folder) |
|||
|
|||
# if the folder is found |
|||
if targets[0] is not None: |
|||
|
|||
uri=targets[1] |
|||
|
|||
# if quiet do not prompt |
|||
if quietmode: |
|||
areyousure="Y" |
|||
else: |
|||
|
|||
if version > 2: |
|||
areyousure=input("Are you sure you want to delete the folder and its contents? (Y)") |
|||
else: |
|||
areyousure=raw_input("Are you sure you want to delete the folder and its contents? (Y)") |
|||
|
|||
if areyousure.upper() == 'Y': |
|||
|
|||
#delete folder content, recursive call returns all children |
|||
reqval=uri+"/members?recursive=true" |
|||
reqtype='get' |
|||
allchildren=callrestapi(reqval,reqtype) |
|||
|
|||
# get all child items |
|||
if 'items' in allchildren: |
|||
|
|||
itemlist=allchildren['items'] |
|||
|
|||
for children in itemlist: |
|||
|
|||
#if it is a report |
|||
if children['contentType']=='report': |
|||
|
|||
linklist=children['links'] |
|||
|
|||
for linkval in linklist: |
|||
|
|||
#find the delete method and call it |
|||
if linkval['rel']=='deleteResource': |
|||
reqval=(linkval['uri']) |
|||
reqtype=(linkval['method']).lower() |
|||
callrestapi(reqval,reqtype) |
|||
|
|||
print("Deleting folder= "+ path_to_folder+" "+uri) |
|||
|
|||
reqval=uri+"?recursive=true" |
|||
reqtype='delete' |
|||
callrestapi(reqval,reqtype) |
|||
print('Folder Deleted.') |
|||
|
|||
else: |
|||
print("Good thing I asked!") |
|||
@ -0,0 +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) |
|||
@ -0,0 +1,52 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# getconfigurationproperties.py |
|||
# December 2017 |
|||
# |
|||
# pass in the coniguration definition and return the properties |
|||
# |
|||
# Change History |
|||
# |
|||
# 27JAN2017 Comments added |
|||
# |
|||
# |
|||
# 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 Python modules |
|||
from __future__ import print_function |
|||
import argparse |
|||
import pprint |
|||
pp = pprint.PrettyPrinter(indent=4) |
|||
|
|||
from sharedfunctions import callrestapi, printresult |
|||
|
|||
|
|||
parser = argparse.ArgumentParser(description="Return a set of configuration properties") |
|||
parser.add_argument("-c","--configuration", help="Enter the configuration definition.",required='True') |
|||
parser.add_argument("-o","--output", help="Output Style", choices=['csv','json','simple'],default='json') |
|||
|
|||
args = parser.parse_args() |
|||
configurationdef=args.configuration |
|||
output_style=args.output |
|||
|
|||
reqval="/configuration/configurations?definitionName="+configurationdef |
|||
|
|||
configvalues=callrestapi(reqval,'get') |
|||
|
|||
printresult(configvalues,output_style) |
|||
|
|||
@ -0,0 +1,47 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# getfolder.py |
|||
# December 2017 |
|||
# |
|||
# getfolderid is a wrapper which sets up the command line arguements and then calls the getfolderid function |
|||
# the function returns a folderid and uri when passed the path to the folder in Viya |
|||
# |
|||
# Change History |
|||
# |
|||
# 27JAN2017 Comments added |
|||
# |
|||
# |
|||
# 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 getfolderid |
|||
|
|||
# setup command-line arguements |
|||
parser = argparse.ArgumentParser() |
|||
parser.add_argument("-f","--folderpath", help="Enter the path to the viya folder.",required='True') |
|||
args = parser.parse_args() |
|||
path_to_folder=args.folderpath |
|||
|
|||
# call the get folderid function and pass it the entered path |
|||
targets=getfolderid(path_to_folder) |
|||
|
|||
#print results if any are returned |
|||
if targets[0] is not None: |
|||
print("Id = "+targets[0]) |
|||
print("URI = "+targets[1]) |
|||
print("Path = "+targets[2]) |
|||
@ -0,0 +1,59 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# getruleid.py |
|||
# December 2017 |
|||
# |
|||
# getruleid pass in a uri and identity and return the rule id |
|||
# |
|||
# Change History |
|||
# |
|||
# 27JAN2017 Comments added |
|||
# 18JUN2018 Output 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() |
|||
|
|||
parser.add_argument("-u","--objecturi", help="Enter the objecturi.",required='True') |
|||
parser.add_argument("-p","--principal", help="Enter the identity name or authenticatedUsers, everyone or guest") |
|||
parser.add_argument("-o","--output", help="Output Style", choices=['csv','json','simple'],default='json') |
|||
|
|||
args = parser.parse_args() |
|||
objuri=args.objecturi |
|||
ident=args.principal |
|||
output_style=args.output |
|||
|
|||
if ident.lower()=='authenticatedusers': ident='authenticatedUsers' |
|||
|
|||
if ident=='guest' or ident=='everyone' or ident=='authenticatedUsers': |
|||
reqval= "/authorization/rules?filter=and(eq(principalType,'"+ident+"'),eq(objectUri,'"+objuri+"'))" |
|||
else: |
|||
reqval= "/authorization/rules?filter=and(eq(principal,'"+ident+"'),eq(objectUri,'"+objuri+"'))" |
|||
|
|||
reqtype='get' |
|||
|
|||
result=callrestapi(reqval,reqtype) |
|||
|
|||
#print("ruleid= "+result['items'][0]['id']) |
|||
|
|||
# print rest call results |
|||
printresult(result,output_style) |
|||
@ -0,0 +1,72 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# listrules.py |
|||
# August 2018 |
|||
# |
|||
# listrulesforidentity |
|||
# |
|||
# 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 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 |
|||
|
|||
# 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 |
|||
result=callrestapi(reqval,reqtype) |
|||
|
|||
#print the result |
|||
printresult(result,output_style) |
|||
|
|||
@ -0,0 +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: |
|||
print('ERROR: '+fname+' does not exist') |
|||
@ -0,0 +1,99 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# movecontent.py |
|||
# December 2017 |
|||
# |
|||
# Moves content from one folder to another |
|||
# |
|||
# Change History |
|||
# |
|||
# 27JAN2018 Comments added |
|||
# 03Feb2018 Added quiet mode |
|||
# 03Mar2018 Made prompt comparison case-insensitive |
|||
# |
|||
# |
|||
# 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 Python modules |
|||
|
|||
import argparse, sys |
|||
|
|||
from sharedfunctions import getfolderid, callrestapi |
|||
|
|||
# get python version |
|||
version=int(str(sys.version_info[0])) |
|||
|
|||
parser = argparse.ArgumentParser(description="Move content from a source to a target folder") |
|||
parser.add_argument("-s","--sourcefolder", help="Enter the path to the source folder.",required='True') |
|||
parser.add_argument("-t","--targetfolder", help="Enter the path to the source folder.",required='True') |
|||
parser.add_argument("-q","--quiet", help="Suppress the are you sure prompt.", action='store_true') |
|||
args = parser.parse_args() |
|||
|
|||
source=args.sourcefolder |
|||
target=args.targetfolder |
|||
|
|||
quietmode=args.quiet |
|||
|
|||
sourceinfo=getfolderid(source) |
|||
targetinfo=getfolderid(target) |
|||
|
|||
if sourceinfo[0] is not None: |
|||
|
|||
id=sourceinfo[0] |
|||
|
|||
if quietmode: |
|||
areyousure="Y" |
|||
else: |
|||
|
|||
if version > 2: |
|||
areyousure=input("Are you sure you want to move content from "+source+" to "+target+"? (Y)") |
|||
else: |
|||
areyousure=raw_input("Are you sure you want to move content from "+source+" to "+target+"? (Y)") |
|||
|
|||
if areyousure.upper() == 'Y': |
|||
|
|||
# get all the content in folder |
|||
reqtype='get' |
|||
reqval='/folders/folders/'+id+"/members" |
|||
members=callrestapi(reqval,reqtype) |
|||
|
|||
# create a list of items |
|||
items=members["items"] |
|||
|
|||
for item in items: |
|||
|
|||
# delete from folder |
|||
|
|||
reqtype="delete" |
|||
reqval='/folders/folders/'+id+"/members/"+item["id"] |
|||
rc=callrestapi(reqval,reqtype) |
|||
|
|||
#build dictionary of item |
|||
|
|||
thisitem={"uri":item["uri"],"name":item["name"],"type":item["type"],"contentType":item["contentType"]} |
|||
|
|||
#add to new folder |
|||
|
|||
reqtype="post" |
|||
reqval="/folders/folders/"+targetinfo[0]+"/members" |
|||
rc=callrestapi(reqval,reqtype,data=thisitem) |
|||
|
|||
print("NOTE: content moved between folder "+source+" and "+target) |
|||
|
|||
|
|||
else: |
|||
print("Good thing I asked!") |
|||
@ -0,0 +1,521 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# sharedfunctions.py |
|||
# December 2017 |
|||
# |
|||
# A set of shared functions used by the piviyatool which makes REST calls to supplement the VIYA CLI |
|||
# |
|||
# callrestapi is the core function it will accept a method endpoint and optionally a python dictionary as input |
|||
# getfolderid returns a folder id if it is passed the path to the viya folder |
|||
# getebaseuri returns the base url for the service from the default profile |
|||
# getauthtoken returns the authentication token created by the CLI call sas-admin auth login |
|||
# getinputjson converts the input json to a python dictionary |
|||
# |
|||
# Change History |
|||
# |
|||
# 27JAN2018 Comments added |
|||
# 29JAN2018 Added simpleresults function |
|||
# 31JAN2018 Added the ability to pass contenttype to call_rest_api (now callrestapi) |
|||
# 31JAN2018 Improved error handling of call_rest_api (now callrestapi) |
|||
# 31JAN2018 Deal with situation where json is not returned |
|||
# 31JAN2018 Fix a bug when neither json or text is returned |
|||
# 02FEB2018 Fix a bug when text is returned |
|||
# 12MAR2018 Made simple result print generic |
|||
# 20MAR2018 Added some comments |
|||
# 20MAR2018 Handle errors when profile and authentication token do not exist |
|||
# 20May2018 Fixed bug in authentication check |
|||
# 01jun2018 Deal with empty profile error |
|||
# 23oct2018 Added print result function |
|||
# 23oct2018 Added print csv |
|||
# 28oct2018 Added stop on error to be able to override stopping processing when an error occurs |
|||
# 20nov2018 Updated so that multiple profiles can be used |
|||
|
|||
# |
|||
# 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. |
|||
|
|||
|
|||
# Import Python modules |
|||
from __future__ import print_function |
|||
import requests |
|||
import sys |
|||
import json |
|||
import pprint |
|||
import os |
|||
|
|||
pp = pprint.PrettyPrinter(indent=4) |
|||
|
|||
# validate rest api is not used at this time |
|||
# not used |
|||
|
|||
def validaterestapi(baseurl, reqval, reqtype, data={}): |
|||
|
|||
global result |
|||
|
|||
print("The request is a "+reqtype+" request: ",baseurl+reqval) |
|||
|
|||
json_data=json.dumps(data, ensure_ascii=False) |
|||
|
|||
print("Data for Request:") |
|||
print(json_data) |
|||
|
|||
if (reqtype !="get" or reqtype !="post" or reqtype!="delete" or reqtype!="put"): |
|||
print("NOTE: Invalid method") |
|||
|
|||
return; |
|||
|
|||
# callrestapi |
|||
# this is the main function called many other programs and by the callrestapi program to make the REST calls |
|||
# change history |
|||
# 01dec2017 initial development |
|||
# 28oct2018 Added stop on error to be able to override stopping processing when an error occurs |
|||
|
|||
def callrestapi(reqval, reqtype, acceptType='application/json', contentType='application/json',data={},stoponerror=1): |
|||
|
|||
|
|||
# get the url from the default profile |
|||
baseurl=getbaseurl() |
|||
|
|||
# get the auth token |
|||
oaval=getauthtoken(baseurl) |
|||
|
|||
# build the authorization header |
|||
head= {'Content-type':contentType,'Accept':acceptType} |
|||
head.update({"Authorization" : oaval}) |
|||
|
|||
# maybe this can be removed |
|||
global result |
|||
|
|||
# sereliaze the data string for the request to json format |
|||
json_data=json.dumps(data, ensure_ascii=False) |
|||
|
|||
# call the rest api using the parameters passed in and the requests python library |
|||
|
|||
if reqtype=="get": |
|||
ret = requests.get(baseurl+reqval,headers=head,data=json_data) |
|||
elif reqtype=="post": |
|||
ret = requests.post(baseurl+reqval,headers=head,data=json_data) |
|||
elif reqtype=="delete": |
|||
ret = requests.delete(baseurl+reqval,headers=head,data=json_data) |
|||
elif reqtype=="put": |
|||
ret = requests.put(baseurl+reqval,headers=head,data=json_data) |
|||
else: |
|||
result=None |
|||
print("NOTE: Invalid method") |
|||
sys.exit() |
|||
|
|||
|
|||
# response error if status code between these numbers |
|||
if (400 <= ret.status_code <=599): |
|||
|
|||
print(ret.text) |
|||
result=None |
|||
if stoponerror: sys.exit() |
|||
|
|||
# return the result |
|||
else: |
|||
# is it json |
|||
try: |
|||
result=ret.json() |
|||
except: |
|||
# is it text |
|||
try: |
|||
result=ret.text |
|||
except: |
|||
result=None |
|||
print("NOTE: No result to print") |
|||
|
|||
|
|||
|
|||
return result; |
|||
|
|||
# getfolderid |
|||
# when a Viya content path is passed in return the id, path and uri |
|||
# change history |
|||
# 01dec2017 initial development |
|||
|
|||
def getfolderid(path): |
|||
|
|||
# build the request parameters |
|||
reqval="/folders/folders/@item?path="+path |
|||
reqtype='get' |
|||
|
|||
callrestapi(reqval,reqtype) |
|||
|
|||
if result==None: |
|||
print("NOTE: Folder'"+path+"' not found.") |
|||
targetid=None |
|||
targetname=None |
|||
targeturi=None |
|||
else: |
|||
targetid=result['id'] |
|||
targetname=result['name'] |
|||
targeturi="/folders/folders/"+targetid |
|||
|
|||
return [targetid,targeturi,targetname] |
|||
|
|||
|
|||
# getbaseurl |
|||
# from the default profile return the baseurl of the Viya server |
|||
# change history |
|||
# 01dec2017 initial development |
|||
# 01jun2018 Deal with empty profile error |
|||
# 20nov2018 Use the SAS_CLI_PROFILE env variable |
|||
|
|||
|
|||
def getbaseurl(): |
|||
|
|||
# check that profile file is available and can be read |
|||
|
|||
# note the path to the profile is hard-coded right now |
|||
endpointfile=os.path.join(os.path.expanduser('~'),'.sas','config.json') |
|||
access_file=file_accessible(endpointfile,'r') |
|||
|
|||
#profile does not exist |
|||
if access_file==False: |
|||
print("ERROR: Cannot read CLI profile at:",endpointfile,". Recreate profile with sas-admin profile init.") |
|||
sys.exit() |
|||
|
|||
#profile is empty file |
|||
if os.stat(endpointfile).st_size==0: |
|||
print("ERROR: Cannot read CLI profile empty file at:",endpointfile,". Recreate profile with sas-admin profile init.") |
|||
sys.exit() |
|||
|
|||
# get json from profile |
|||
with open(endpointfile) as json_file: |
|||
data = json.load(json_file) |
|||
|
|||
# get the profile environment variable to use it |
|||
# if it is not set default to the default profile |
|||
|
|||
cur_profile=os.environ.get("SAS_CLI_PROFILE","Default") |
|||
#print("URL: ",cur_profile ) |
|||
|
|||
# check that information is in profile |
|||
if cur_profile in data: |
|||
baseurl=data[cur_profile]['sas-endpoint'] |
|||
else: |
|||
|
|||
baseurl=None |
|||
print("ERROR: profile "+cur_profile+" does not exist. Recreate profile with sas-admin profile init.") |
|||
sys.exit() |
|||
|
|||
|
|||
return baseurl |
|||
|
|||
|
|||
# getauthtoken |
|||
# from the stored auth file get the authentication token for the request header |
|||
# change history |
|||
# 01dec2017 initial development |
|||
# return oaval=None when no authtoken retrieved |
|||
# 20nov2018 Use the SAS_CLI_PROFILE env variable |
|||
|
|||
def getauthtoken(baseurl): |
|||
|
|||
|
|||
#get authentication information for the header |
|||
credential_file=os.path.join(os.path.expanduser('~'),'.sas','credentials.json') |
|||
|
|||
# check that credential file is available and can be read |
|||
access_file=file_accessible(credential_file,'r') |
|||
|
|||
if access_file==False: |
|||
oaval=None |
|||
print("ERROR: Cannot read authentication credentials at: ", credential_file) |
|||
print("ERROR: Try refreshing your token with sas-admin auth login") |
|||
sys.exit() |
|||
|
|||
with open(credential_file) as json_file: |
|||
data = json.load(json_file) |
|||
type(data) |
|||
|
|||
# the sas-admin profile init creates an empty credential file |
|||
# check that credential is in file, if it is add it to the header, if not exit |
|||
|
|||
# get the profile environment variable to use it |
|||
# if it is not set default to the default profile |
|||
|
|||
cur_profile=os.environ.get("SAS_CLI_PROFILE","Default") |
|||
|
|||
#print("LOGON: ", cur_profile ) |
|||
|
|||
if cur_profile in data: |
|||
|
|||
oauthToken=data[cur_profile]['access-token'] |
|||
|
|||
oauthTokenType="bearer" |
|||
|
|||
oaval=oauthTokenType + ' ' + oauthToken |
|||
|
|||
head= {'Content-type':'application/json','Accept':'application/json' } |
|||
head.update({"Authorization" : oaval}) |
|||
|
|||
# test a connection to rest api if it fails exit |
|||
r = requests.get(baseurl,headers=head) |
|||
|
|||
if (400 <= r.status_code <=599): |
|||
|
|||
oaval=None |
|||
print(r.text) |
|||
print("ERROR: cannot connect to "+baseurl+" is your token expired?") |
|||
print("ERROR: Try refreshing your token with sas-admin auth login") |
|||
sys.exit() |
|||
else: |
|||
|
|||
oaval=None |
|||
print("ERROR: access token not in file: ", credential_file) |
|||
print("ERROR: Try refreshing your token with sas-admin auth login") |
|||
sys.exit() |
|||
|
|||
return oaval |
|||
|
|||
# getinputjson |
|||
# load the returned json to a python dictionary |
|||
# change history |
|||
# 01dec2017 initial development |
|||
|
|||
def getinputjson(input_file): |
|||
|
|||
with open(input_file) as json_file: |
|||
inputdata = json.load(json_file) |
|||
|
|||
return inputdata |
|||
|
|||
# simpleresults |
|||
# take the complex json and create a simple print of the results |
|||
# change history |
|||
# 01dec2017 initial development |
|||
|
|||
def simpleresults(resultdata): |
|||
|
|||
# print a simplification of the json results |
|||
|
|||
|
|||
# list of items returned by rest call |
|||
if 'items' in resultdata: |
|||
|
|||
total_items=resultdata['count'] |
|||
|
|||
returned_items=len(resultdata['items']) |
|||
|
|||
if total_items == 0: print("Note: No items returned.") |
|||
|
|||
for i in range(0,returned_items): |
|||
|
|||
print ("=====Item ",i,"=======") |
|||
|
|||
pairs=resultdata['items'][i] |
|||
|
|||
for key,val in pairs.items(): |
|||
|
|||
if key != 'links': |
|||
print(key,end="") |
|||
print(" = ", val) |
|||
|
|||
print("Result Summary: Total items available: ",total_items ,"Total items returned: ", returned_items) |
|||
|
|||
elif 'id' in resultdata: #one item returned by rest call |
|||
|
|||
for key,val in resultdata.items(): |
|||
|
|||
if key != 'links': |
|||
print(key,end="") |
|||
print(" = ", val) |
|||
|
|||
else: |
|||
print("NOTE: No JSON Results Found") |
|||
|
|||
|
|||
|
|||
# tableresults |
|||
# take the complex json and create a simple table of the results |
|||
# change history |
|||
# 01aug2018 initial development |
|||
|
|||
def csvresults(resultdata): |
|||
|
|||
#print(resultdata) |
|||
|
|||
if 'items' in resultdata: |
|||
|
|||
total_items=resultdata['count'] |
|||
|
|||
returned_items=len(resultdata['items']) |
|||
|
|||
if total_items == 0: print("Note: No items returned.") |
|||
|
|||
for i in range(0,returned_items): |
|||
|
|||
pairs=resultdata['items'][i] |
|||
|
|||
#test=pairs.get('description') |
|||
#if test==None: pairs['description']='None' |
|||
|
|||
for key,val in pairs.items(): |
|||
|
|||
if i==0: print(key,',',end="") |
|||
|
|||
print("\n",end="") |
|||
|
|||
for key,val in pairs.items(): |
|||
|
|||
if key != 'links': |
|||
print('"',val,'",',end="") |
|||
|
|||
print("\n",end="") |
|||
|
|||
|
|||
elif 'id' in resultdata: #one item returned by rest call |
|||
|
|||
for key,val in resultdata.items(): |
|||
|
|||
if key != 'links': |
|||
print(key,',',end="") |
|||
|
|||
print("\n",end="") |
|||
|
|||
for key,val in resultdata.items(): |
|||
|
|||
if key != 'links': |
|||
print('"',val,'",',end="") |
|||
|
|||
print("\n",end="") |
|||
|
|||
else: |
|||
print("NOTE: No JSON Results Found") |
|||
|
|||
|
|||
# file_accessible |
|||
# Check if a file exists and is accessible. |
|||
# change history |
|||
# 01dec2017 initial development |
|||
|
|||
def file_accessible(filepath, mode): |
|||
|
|||
try: |
|||
f = open(filepath, mode) |
|||
f.close() |
|||
except IOError as e: |
|||
return False |
|||
|
|||
return True |
|||
|
|||
|
|||
# printresult |
|||
# prints the results in the style requested |
|||
# change history |
|||
# 28oct2018 initial development |
|||
|
|||
def printresult(result,output_style): |
|||
|
|||
# print rest call results |
|||
if type(result) is dict: |
|||
|
|||
if output_style=='simple': |
|||
simpleresults(result) |
|||
elif output_style=='csv': |
|||
csvresults(result) |
|||
else: |
|||
print(json.dumps(result,indent=2)) |
|||
else: print(result) |
|||
|
|||
|
|||
|
|||
# getprofileinfo |
|||
# prints the token expiration, endpoint and current user |
|||
# change history |
|||
# 20nov2018 initial development |
|||
|
|||
|
|||
def getprofileinfo(myprofile): |
|||
|
|||
|
|||
|
|||
#get authentication information for the header |
|||
credential_file=os.path.join(os.path.expanduser('~'),'.sas','credentials.json') |
|||
|
|||
# check that credential file is available and can be read |
|||
access_file=file_accessible(credential_file,'r') |
|||
|
|||
if access_file==False: |
|||
print("ERROR: Cannot read authentication credentials at: ", credential_file) |
|||
print("ERROR: Try refreshing your token with sas-admin auth login") |
|||
sys.exit() |
|||
|
|||
with open(credential_file) as json_file: |
|||
data = json.load(json_file) |
|||
type(data) |
|||
|
|||
# the sas-admin profile init creates an empty credential file |
|||
# check that credential is in file, if it is add it to the header, if not exit |
|||
|
|||
# get the profile environment variable to use it |
|||
# if it is not set default to the default profile |
|||
|
|||
|
|||
if myprofile in data: |
|||
|
|||
expiry=data[myprofile]['expiry'] |
|||
print("Note your authentication token expires at: "+expiry) |
|||
|
|||
else: |
|||
|
|||
print("ERROR: access token not in file: ", credential_file) |
|||
print("ERROR: Try refreshing your token with sas-admin auth login") |
|||
sys.exit() |
|||
|
|||
|
|||
# note the path to the profile is hard-coded right now |
|||
endpointfile=os.path.join(os.path.expanduser('~'),'.sas','config.json') |
|||
access_file=file_accessible(endpointfile,'r') |
|||
|
|||
#profile does not exist |
|||
if access_file==False: |
|||
print("ERROR: Cannot read CLI profile at:",endpointfile,". Recreate profile with sas-admin profile init.") |
|||
sys.exit() |
|||
|
|||
#profile is empty file |
|||
if os.stat(endpointfile).st_size==0: |
|||
print("ERROR: Cannot read CLI profile empty file at:",endpointfile,". Recreate profile with sas-admin profile init.") |
|||
sys.exit() |
|||
|
|||
# get json from profile |
|||
with open(endpointfile) as json_file: |
|||
data = json.load(json_file) |
|||
|
|||
|
|||
# check that information is in profile |
|||
if myprofile in data: |
|||
baseurl=data[myprofile]['sas-endpoint'] |
|||
print("Endpoint is: "+baseurl) |
|||
else: |
|||
print("ERROR: profile "+myprofile+" does not exist. Recreate profile with sas-admin profile init.") |
|||
|
|||
# build the request parameters |
|||
reqval="/identities/users/@currentUser" |
|||
reqtype='get' |
|||
|
|||
result=callrestapi(reqval,reqtype) |
|||
|
|||
if result==None: |
|||
print("NOTE: Not logged in.") |
|||
|
|||
else: |
|||
print("Logged on as id: "+ result['id']) |
|||
print("Logged on as name: "+result['name']) |
|||
|
|||
|
|||
|
|||
@ -0,0 +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) |
|||
@ -0,0 +1,114 @@ |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
# |
|||
# testfolderaccess.py |
|||
# January 2018 |
|||
# |
|||
# Usage: |
|||
# python testfolderaccess.py -f folderpath -n name_of_user_or_group -t user|group -s grant|prohibit -m permission [-q] [-d] |
|||
# |
|||
# |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION |
|||
clidir='/opt/sas/viya/home/bin/' |
|||
debug=False |
|||
|
|||
# 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="Enter the path to the viya folder.",required='True') |
|||
parser.add_argument("-n","--name", help="Enter the name of the user or group to test.",required='True') |
|||
parser.add_argument("-t","--principaltype", help="Enter the type of principal to test: user or group.",required='True',choices=['user','group']) |
|||
parser.add_argument("-s","--setting", help="Enter grant or prohibit as the expected setting for the permission being tested.",required='True', choices=['grant','prohibit']) |
|||
parser.add_argument("-m","--permission", help="Enter the permission to test.",required='True', choices=["read","update","delete","add","secure","remove"]) |
|||
parser.add_argument("-q","--quiet", action='store_true') |
|||
parser.add_argument("-d","--debug", action='store_true') |
|||
args = parser.parse_args() |
|||
path_to_folder=args.folderpath |
|||
name=args.name |
|||
principaltype=args.principaltype |
|||
setting=args.setting |
|||
permission=args.permission |
|||
quiet=args.quiet |
|||
debug=args.debug |
|||
|
|||
|
|||
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]) |
|||
|
|||
endpoint='/authorization/decision' |
|||
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":[folder_uri]} |
|||
|
|||
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'][folder_uri] |
|||
|
|||
#print('e is a '+type(e).__name__+' object') #e is a list object |
|||
|
|||
principal_found=False |
|||
|
|||
for pi in e: |
|||
#print pi['principal'] |
|||
# Test whether principal has a name: authenticatedusers and guest do not have a name key |
|||
if 'name' in pi['principal']: |
|||
if (pi['principal']['name'].lower() == name.lower()): |
|||
#print(pi['principal']['name']+':'+pi[permission.lower()]['result']) |
|||
principal_found=True |
|||
if (pi[permission.lower()]['result'] == setting.lower()): |
|||
if not quiet: |
|||
print('TEST PASSED: the effective '+permission.lower()+' permission for '+pi['principal']['name']+' on folder '+path_to_folder+' is '+pi[permission.lower()]['result']) |
|||
else: |
|||
raise Exception('TEST FAILED: the effective '+permission.lower()+' permission for '+pi['principal']['name']+' on folder '+path_to_folder+' is '+pi[permission.lower()]['result']+', not '+setting.lower()) |
|||
|
|||
if not principal_found: |
|||
raise Exception('No direct or inherited authorization rules found for \''+name+'\' on folder '+path_to_folder+'. Please check that you spelled the principal name correctly, and specified the correct principal type - user or group.') |
|||
@ -0,0 +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 |
|||
@ -0,0 +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 |
|||
|
|||
@ -0,0 +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) |
|||
@ -0,0 +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) |
|||
else: print("Cannot set preferences for a group "+id ) |
|||
Loading…
Reference in new issue