Creating user in Jenkins via API - api

I was wondering if I can create a new user in Jenkins using its API. I can create jobs but the API docs for Jenkins don't have anything related to user creation.
Actually, I have to create a new user followed by creating a new job for that user, all of this using an API.

You're right, there is no explicit CLI command for adding a user. But you could use groovy script for this (using the CLI for execution).
The details depend on how your Jenkins is configured.
For example, if your Jenkins uses its own user database, then you could add a new user by the following CLI call:
echo 'jenkins.model.Jenkins.instance.securityRealm.createAccount("user1", "password123")' |
java -jar jenkins-cli.jar -s http://localhost/ groovy =
This shell command will create a new user with login "user1" and password "password123".
Here echo feeds a line of groovy code to a Jenkins CLI (note that = means that CLI should receive code from STDIN).
Also groovy script allows to manage user permissions, however, the exact code depends on what authorization strategy is used. You could use this sample script as a start.

This is how to create user after installation:
echo 'jenkins.model.Jenkins.instance.securityRealm.createAccount("user", "password")' | java -jar jenkins-cli.jar -auth admin:c3a5dcd6bc3f45ee8d6c9f0f5abc14c0 -s http://localhost:8080/ groovy =
Where c3a5dcd6bc3f45ee8d6c9f0f5abc14c0 is automatically generated password present in log or in file (for ubuntu): /var/lib/jenkins/secrets/initialAdminPassword

echo and pipe didn't work on my Windows, so I ended up using a script file instead. It's also easier to add more logic in the script file. The script below will check existing user before adding a new user, and then set the user's email after account creation and give READ access using Matrix-based security. You can run it by saving the script into a file, say user-creation.groovy, and then run the following,
java -jar jenkins-cli.jar -s http://localhost/ groovy user-creation.groovy testUser testPassword testEmail#testEmail.com
import hudson.model.*
import hudson.security.*
import hudson.tasks.Mailer
def userId = args[0]
def password = args[1]
def email = args[2]
def instance = jenkins.model.Jenkins.instance
def existingUser = instance.securityRealm.allUsers.find {it.id == userId}
if (existingUser == null) {
def user = instance.securityRealm.createAccount(userId, password)
user.addProperty(new Mailer.UserProperty(email));
def strategy = (GlobalMatrixAuthorizationStrategy) instance.getAuthorizationStrategy()
strategy.add(Hudson.READ, userId)
instance.setAuthorizationStrategy(strategy)
instance.save()
}

I managed to get the following python snippet to create a user with ssh-key:
import json
import requests
def main():
data = {
'credentials': {
'scope': "GLOBAL",
'username': "jenkins",
'privateKeySource': {
'privateKey': "-----BEGIN RSA PRIVATE KEY-----\nX\n-----END RSA PRIVATE KEY-----",
'stapler-class': "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource"
},
'stapler-class': "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
}
}
payload = {
'json': json.dumps(data),
'Submit': "OK",
}
r = requests.post("http://%s:%d/credential-store/domain/_/createCredentials" % (HOSTNAME, 8080), data=payload)
if r.status_code != requests.codes.ok:
print r.text
It is sort of like a REST interface except that one has to know the internals of the code and the names of the classes that the objects are supposed to decode to.
I'm trying to configure jenkins from an ansible script (running externally to the jenkins server); since the java cli doesn't support creating the credentials the python snippet seems the way to go.

Building on #vitaleek's answer the following will grab the default admin credentials from the file and create a new user:
pass=`sudo cat /var/lib/jenkins/secrets/initialAdminPassword` && echo 'jenkins.model.Jenkins.instance.securityRealm.createAccount("user1", "password123")' | sudo java -jar jenkins-cli.jar -auth admin:$pass -s http://localhost:8080/ groovy =
If you're like me and you couldn't find the jenkins-cli.jar at first, that can be pulled from your Jenkins server as follows:
curl -O http://127.0.0.1:8080/jnlpJars/jenkins-cli.jar

Related

Download file over HTTPS and SSO in groovy avoiding server certificate validation

I want to download a file in groovy over a connection that is both using single sign on (SSO) over HTTPS (SSL) is there an easy way to do this. I'm not intending to build a full blown application so security is not as much of a concern.
def data = new URL("https://server/context/servlet?param1=value1").getText()
print data
I currently do the download using curl but would ideally not have to call curl. current used call below.
curl --negotiate -u user:pass -L --insecure -o filename.txt "https://server/context/servlet?param1=value1"
Two key points to the solution i'm looking for
- It does not involve making a system call to curl
- It does not include manually setting up a certificate.
Would consider libraries.
To avoid the SSL PKIX validation check, in Groovy, you can implement a X509TrustManager in the same way that you do it in Java.
Note that this disable the validation server certificate validation, therefore it's a security risk:
import javax.net.ssl.*
// create a TrustManager to avoid PKIX path validation
def trustManager = [
checkClientTrusted: { chain, authType -> },
checkServerTrusted: { chain, authType -> },
getAcceptedIssuers: { null }
] as X509TrustManager
// creat a sslCtx to use "lax" trustManager
def context = SSLContext.getInstance("TLS")
context.init(null, [trustManager] as TrustManager[], null)
// set as default ssl context
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory())
// finally you can connect to the server "insecurely"
def data = new URL("https://server/context/servlet?param1=value1").getText()
print data
About your second question, to provide a basic authentication like curl does with --user argument, you can set a default user/password for your connections using Authenticator class:
Authenticator.setDefault (new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication ("user", "pass".toCharArray())
}
})
Note that is possible to do so on other ways in Groovy using some libraries, but this is a possible way using standard Java classes.

Cant connect to database pymysql using a .my.cnf file

This function should connect to database because I've used an exception I'll get my own created message, when incorrect input is found. But when I remove try and except I get : "Acces denied for /'user/'#'/'localhost' (using password : NO).
It seems that it doesnt read my password I have no clue why a little help will be appreciated. Without this file connection to the database works fine without any errors.
try:
self.conn = pymysql.connect(read_default_file="~/my.cnf")
self.curr = self.conn.cursor()
except pymysql.err.OperationalError :
sys.exit("Invalid Input: Wrong username/database or password found, please try again")
Information found in the .my.cnf file is :
[client]
host = 'localhost'
port = 3306
user = myusername
password = "mypassword"
You used:
pymysql.connect(read_default_file="~/my.cnf")
You should have used:
pymysql.connect(read_default_file='~/.my.cnf')
-------------------------------------^
Note the missing dot in the filename. You were just loading a different or non existent file.
The ~ is being expanded as shown here:
https://github.com/PyMySQL/PyMySQL/blob/18b62f6e1d6f65b403c9e8b650f4c3bb27b665e7/pymysql/connections.py#L619
Also I can confirm that no quotes or spaces are required in the .my.cnf file:
echo -e "[client]\nuser=root\npassword=defaultrootpwd" > ~/.my.cnf
First off, can you connect to your database using the following command.
import pymysql
conn = pymysql.connect(host='localhost',
port=3306,
user='myusername',
passwd='mypasswd')
If that doesn't work, then you might have some other problem (for instance, your database may be configured to only connect via socket for local clients)
As for the config file, I think this will work if you remove the quotation marks, like this:
[client]
host = localhost
port = 3306
user = myusername
password = mypassword
I saved your config file as test.cnf and ran the following code
# test.cnf
[client]
host = 'localhost'
port = 3306
user = myusername
password = "mypassword"
$ python3
>>> import configparser
>>> reader = configparser.RawConfigParser()
>>> reader.read('test.cnf')
>>> reader.get('client', 'host')
"'localhost'"
>>> reader.get('client', 'user')
'myusername'
As you can see, the config parser is treating quotes as part of the value.
Update: Workaround
The OP mentioned the provided solution (i.e. removal of quotes did not solve the problem he was facing). Here's a workaround that achieves separation of configuration / connection properties from program logic.
Save the configuration in a python file called dbsettings.py.
# dbsettings.py
connection_properties = {
'host': 'localhost',
'port': 3306,
'user': 'myusername',
'passwd': 'mypassword'
}
And then in your main program use the following lines for setting up the connection.
try:
from dbsettings import connection_properties
self.conn = pymysql.connect(**connection_properties)
self.curr = self.conn.cursor()
except pymysql.err.OperationalError :
sys.exit("Invalid Input: Wrong username/database or password found, please try again")
If your entire program is written in python, then this allows the same separation of connection / config info from program logic as using the my.cnf method, and it is just as flexible if not mroe so. However, if other non python scripts need to reference the my.cnf file, then you'll have to maintain two separate mysql config files.

Only show repos in gitweb that user has access to via Gitolite

I have gitolite setup and working with SSH key based auth. I can control access to repos via the gitolite-admin.git repo and the conf file. All of this works great over SSH but I would like to use GitWeb as a quick way to view the repos.
GitWeb is working great now but shows all repositories via the web interface. So my goal here is to:
Authenticate users in apache2 via PAM, I already have the Ubuntu server authenticating aginst AD and all the users are available. This should not be an issue.
Use the user name logged in with the check gitolite permissions
Display apropriate REPOS in the web interface.
Does anyone have a starting point for this? The Apache part shouldn't be difficult, and I'll set it to auth all fo the /gitweb/ url. I dont know how to pass that username around and authorize it against gitolite. Any ideas?
Thanks,
Nathan
Yes, it is possible, but you need to complete the gitweb config scripts in order to call gitolite.
The key is in the gitweb_config.perl: if that file exists, gitweb will include and call it.
See my gitweb/gitweb_config.perl file:
our $home_link_str = "ITSVC projects";
our $site_name = "ITSVC Gitweb";
use lib (".");
require "gitweb.conf.pl";
In gitweb/gitweb.conf.pl (custom script), I define the official callback function called by gitweb: export_auth_hook: that function will call gitolite.
use Gitolite::Common;
use Gitolite::Conf::Load;
#$ENV{GL_USER} = $cgi->remote_user || "gitweb";
$export_auth_hook = sub {
my $repo = shift;
my $user = $ENV{GL_USER};
# gitweb passes us the full repo path; so we strip the beginning
# and the end, to get the repo name as it is specified in gitolite conf
return unless $repo =~ s/^\Q$projectroot\E\/?(.+)\.git$/$1/;
# check for (at least) "R" permission
my $ret = &access( $repo, $user, 'R', 'any' );
my $res = $ret !~ /DENIED/;
return ($ret !~ /DENIED/);
};
From the comments:
GL_USER is set because of the line:
$ENV{GL_USER} = $cgi->remote_user || "gitweb";
$cgi->remote_user will pick the environment REMOTE_USER set by any Apache Auth module which has completed the authentication (like in this Apache configuration file).
You can print it with a 'die' line.
"Could not find Gitolite/Rc.pm" means the INC variable used by perl doesn't contain $ENV{GL_LIBDIR}; (set to ~/gitolite/lib or <any_place_where_gitolite_was_installed>/lib).
That is why there is a line in the same gitweb/gitweb.conf.pl file which adds that to INC:
unshift #INC, $ENV{GL_LIBDIR};
use lib $ENV{GL_LIBDIR};
use Gitolite::Rc;
Edit from Nat45928: in my case I needed to insert my home path into all the '#H#' entries. That solved all of my issues right away.

Jenkins: configure slave node address dynamically using command or groovy script

I have kinda ssh slave build jenkins setup.
Jenkins server connect to Mac slave thru ssh. build ios apps there. two remote nodes are configured in Jenkins connected to the Mac.
The Mac has dhcp.
Every time my mac starts I want to run a script that tell the Jenkin server to configure the node's IP address pointing to the dhcp address that the mac receives. Since its dhcp it changes always.
Is possible to configure such? using shell script or perl ...
e.g. http://jenkins-server:8080/computer/mac-slave-enterprise/configure
is the node config url. If its possible to setup by sending host=10.1.2.100 & Submit=Save or something like this?
I found it is possible run Groovy script at
http://jenkins/script
or from mac command line or sh script,
$ curl -d "script=<your_script_here>" http://jenkins/script
I tried to get some info with this code but no luck, seems I have create SSLLauncher, but lost in how to grab a launcher things. There is no direct setHost or setLauncher thing.
following the tutorial at,
https://wiki.jenkins-ci.org/display/JENKINS/Display+Information+About+Nodes
but cannot set the host address.
println("node desc launcher = " + aSlave.getComputer().getLauncher());
//println("node desc launcher = " + aSlave.getComputer().getLauncher().setHost("10.11.51.70"));
println("node launcher host = " + aSlave.getComputer().getLauncher().getHost());
hudson.plugins.sshslaves.SSHLauncher ssl = aSlave.getComputer().getLauncher();
int port = ssl.getPort();
String userName, password, privateKey;
userName = ssl.getUsername();
password = ssl.getPassword();
privateKey = ssl.getPrivatekey();
println("user: "+userName + ", pwd: "+password + ", key: "+privateKey);
// all these values returns null.
Another way would be to just delete the node and recreate it.
Here is some groovy on how to delete it from here:
for (aSlave in hudson.model.Hudson.instance.slaves) {
if (aSlave.name == "MySlaveToDelete") {
println('====================');
println('Name: ' + aSlave.name);
println('Shutting down node!!!!');
aSlave.getComputer().setTemporarilyOffline(true,null);
aSlave.getComputer().doDoDelete();
}
And here is how to create one (source):
import jenkins.model.*
import hudson.model.*
import hudson.slaves.*
Jenkins.instance.addNode(new DumbSlave("test-script","test slave description","C:\\Jenkins","1",Node.Mode.NORMAL,"test-slave-label",new JNLPLauncher(),new RetentionStrategy.Always(),new LinkedList()))

Couchdb bad_utf8_character_code

I am using couchdb through couchrest in a ruby on rails application. When I try to use futon it alerts with a message box saying bad_utf8_character_code. If I try to access records from rails console using Model.all it raises either of 500:internal server error,
RestClient::ServerBrokeConnection: Server broke connection: or Errno::ECONNREFUSED: Connection refused - connect(2)
Could any one help me to sort this issue?
I ran into this issue. I tried various curl calls to delete, modify, and even just view the offending document. None of them worked. Finally, I decided to pull the documents down to my local machine one at a time, skip the "bad" one, and then replicate from my local out to production.
Disable app (so no more records are being written to the db)
Delete and recreate local database (run these commands in a shell):
curl -X DELETE http://127.0.0.1:5984/mydb
curl -X PUT http://127.0.0.1:5984/mydb
Pull down documents from live to local using this ruby script
require 'bundler'
require 'json'
all_docs = JSON.parse(`curl http://server.com:5984/mydb/_all_docs`)
docs = all_docs['rows']
ids = docs.map{|doc| doc['id']}
bad_ids = ['196ee4a2649b966b13c97672e8863c49']
good_ids = ids - bad_ids
good_ids.each do |curr_id|
curr_doc = JSON.parse(`curl http://server.com:5984/mydb/#{curr_id}`)
curr_doc.delete('_id')
curr_doc.delete('_rev')
data = curr_doc.to_json.gsub("\\'", "\'").gsub('"','\"')
cmd = %Q~curl -X PUT http://127.0.0.1:5984/mydb/#{curr_id} -d "#{data}"~
puts cmd
`#{cmd}`
end
Destroy (delete) and recreate production database (I did this in futon)
Replicate
curl -X POST http://127.0.0.1:5984/_replicate -d '{"target":"http://server.com:5984/mydb", "source":"http://127.0.0.1:5984/mydb"}' -H "Content-Type: application/json"
Restart app