I'm attempting to modify a saved search's permissions through the Splunk Python SDK. Unfortunately, it doesn't seem to be supported out of the box, so I'm trying to do it through the REST API. I asked a previous question (modifying saved search permissions using the python sdk) which showed me how to do it in Java, but I'm having trouble translating that solution to Python. I'm using the urllib2 module. I've had some success with this, but I'm stuck:
def modify_perms( ss ):
service = initSplunk()
zen_config = get_config()
url = "localhost:8089/servicesNS/%s/splenoss/saved/searches/%s/acl" % ("admin", ss)
request = urllib2.Request( url )
base64string = base64.encodestring("admin:changeme")
request.add_header("Authorization", "Basic %s" % base64string)
request.add_header("Referer", url )
# Saved search access params
jwargs = { "sharing" : "app" ,
"modifiable" : "1" ,
"perms.read" : "*" ,
"perms.write" : ["admin" , "power"] ,
#"perms" : {"read":["*"] , "write":["admin" , "power"]} ,
}
#response = urllib2.urlopen( request , **jwargs )
response = urllib2.urlopen( request )
print response.read()
If I don't try to push anything to splunk, I get a response back, which has all of the permission settings. However, once I do try to push jwargs, I get error messages about unexpected keywords. I'm assuming that I'm not passing jwargs correctly, or the way I've built jwargs isn't correct. What am I missing to get this to work in Python?
There are a few changes that would need to be made here-
First, when using urllib2 to post, you'll need to urlencode the data (https://docs.python.org/2/howto/urllib2.html#data)
Secondly, base64.encodestring appears to be appending padding in some manner, you'll need to strip that out.
Lastly, you'll have to add the content type in the header to use the urlencoded data.
Something such as the following should work -
import urllib2
import base64
import urllib
def modify_perms( ss ):
url = "https://localhost:8089/servicesNS/%s/search/saved/searches/%s/acl" % ("admin", ss)
request = urllib2.Request( url )
base64string = base64.encodestring("admin:changeme").strip()
request.add_header("Authorization", "Basic %s" % base64string)
request.add_header("Referer", url )
request.add_header("Content-Type", "application/x-www-form-urlencoded")
kwargs = { "sharing" : "app","owner":"nobody" }
response = urllib2.urlopen( request , urllib.urlencode(kwargs))
print response.read()
modify_perms("test")
This is for a search named test in the search app changing the owner to "nobody" and sharing to "app".
In Splunk 6.4.1, the URL is https://localhost:8089/servicesNS/admin/MYAPP/saved/searches/MYSEARCH/acl,Li
There are a few changes that would need to be made here-
First, when using urllib2 to post, you'll need to urlencode the data (https://docs.python.org/2/howto/urllib2.html#data)
Secondly, base64.encodestring appears to be appending padding in some manner, you'll need to strip that out.
Lastly, you'll have to add the content type in the header to use the urlencoded data.
Something such as the following should work -
import urllib2
import base64
import urllib
def modify_perms( ss ):
url = "https://localhost:8089/servicesNS/%s/search/saved/searches/%s/acl" % ("admin", ss)
request = urllib2.Request( url )
base64string = base64.encodestring("admin:changeme").strip()
request.add_header("Authorization", "Basic %s" % base64string)
request.add_header("Referer", url )
request.add_header("Content-Type", "application/x-www-form-urlencoded")
kwargs = { "sharing" : "app","owner":"nobody" }
response = urllib2.urlopen( request , urllib.urlencode(kwargs))
print response.read()
modify_perms("test")
This is for a search named test in the search app changing the owner to "nobody" and sharing to "app".
By the way if you want to add multiple items (such as for permissions in the following example) use a comma delimited string format
{ "sharing" : "app","owner":"nobody","perms.write":"admin,power,user" }
Can I ask if someone can explain me why you insert this part?
request.add_header("Authorization", "Basic %s" % base64string)
request.add_header("Referer", url )
thank you
Splunk allows for basic HTTP authentication as seen here - http://docs.splunk.com/Documentation/Splunk/latest/RESTUM/RESTusing#Supported_HTTP_methods . Also note the defining RFC for this method here - http://www.rfc-base.org/txt/rfc-1945.txt
Authentication is necessary to access certain REST endpoints in Splunk (most of them actually).
I replaced my perms.write with "admin,power" and I got the same results, I keep getting those unhashable type errors or unexpected keyword errors.
As for splunk.rest, I can import that module just fine. However, when I execute the Simple Request function, it returns errors related to SPLUNK_HOME, as if that variable doesn't exist.
Replace the array for perms.write
with "admin,power"
.
As for the $SPLUNK_HOME
environment variable on Windows, that's probably C:\Program Files\Splunk
. You can simply import splunk.rest
when called from within Splunk's own Python.
This is the content of my request via urllib2:
print request.get_full_url()
print request.get_method()
https://localhost:8089/servicesNS/admin/splenoss/saved/searches/test/acl
POST
{'owner': 'admin', 'perms.write': ['admin', 'power'], 'sharing': 'app', 'perms.read': '*'}
Yes, I've inspected the request and it looks good. If I do not unpack jwargs with **, I will get TypeError: unhashable type when I try to open the url. If I try to build the request using **jwargs instead, it errors out before I can even try to open the url, I get an "unexpected keyword argument" on the first entry in jwargs
Since I'm running Splunk on my Windows machine, I cannot test the curl command. I also couldn't get the splunk.rest module to work correctly, I kept getting KeyError: "SPLUNK_HOME" (I'm guessing that variable doesn't exist with the Windows install)
Did you inspect your request to see what it actually sent as HTTP POST body? Coming from the other direction, did you use a basic client like curl to send a POST manually to validate the body you intend to send?
splunk.rest
sits in $SPLUNK_HOME/Python-2.7/Lib/site-packages/splunk/rest
- part of the Splunk install itself.
there is no splunk.rest module, or splunklib.rest (or at least I can't find it)...what are you using here?
Also, yes I'm missing "owner" in my args, but that's not important. No matter what I put in my args, I always get back an error about unexpected keys
Is your perms.write array translated to "perms.write=admin,power" in the HTTP POST body?
Additionally, you seem to be missing the owner.
Hmm. Here's a simplified excerpt of modifying the permissions from Python I've built a while back:
import splunk.rest as rest
...
aclUrl = "/servicesNS/" + user + "/" + app + "/saved/searches/" + search + "/acl"
postArgs = {"perms.write": perms_write, "perms.read": perms_read, "sharing": "app", "owner": entity.owner}
rest.simpleRequest(aclUrl, sessionKey=sessionKey, postargs=postArgs, method='POST', raiseAllErrors=True)
entity.owner
is the owner of the search (from splunk.entity.getEntity()
), and perms_write/perms_read
are comma-separated strings of roles.
Yes, I do get an error about unexpected keys. However, it complains no matter what I try to pass (if it's a valid key or not)
I'm going to guess that the REST API is complaining about an unexpected modifiable
key. That's not writeable, but rather a calculated part of the response, telling the client if the permissions and sharing of the object allow the current user to modify the object. Setting that doesn't make sense, it's implicitly set of each user through the perms keys.