-
-
Save vitalyisaev2/215f890e75252cd36794221c2debf365 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3 | |
""" | |
Script that creates Personal Access Token for Gitlab API; | |
Tested with: | |
- Gitlab Community Edition 10.1.4 | |
- Gitlab Enterprise Edition 12.6.2 | |
- Gitlab Enterprise Edition 13.4.4 | |
""" | |
import sys | |
import requests | |
from urllib.parse import urljoin | |
from bs4 import BeautifulSoup | |
endpoint = "http://localhost:10080" | |
root_route = urljoin(endpoint, "/") | |
sign_in_route = urljoin(endpoint, "/users/sign_in") | |
pat_route = urljoin(endpoint, "/profile/personal_access_tokens") | |
login = "root" | |
password = "password" | |
def find_csrf_token(text): | |
soup = BeautifulSoup(text, "lxml") | |
token = soup.find(attrs={"name": "csrf-token"}) | |
param = soup.find(attrs={"name": "csrf-param"}) | |
data = {param.get("content"): token.get("content")} | |
return data | |
def obtain_csrf_token(): | |
r = requests.get(root_route) | |
token = find_csrf_token(r.text) | |
return token, r.cookies | |
def sign_in(csrf, cookies): | |
data = { | |
"user[login]": login, | |
"user[password]": password, | |
"user[remember_me]": 0, | |
"utf8": "✓" | |
} | |
data.update(csrf) | |
r = requests.post(sign_in_route, data=data, cookies=cookies) | |
token = find_csrf_token(r.text) | |
return token, r.history[0].cookies | |
def obtain_personal_access_token(name, expires_at, csrf, cookies): | |
data = { | |
"personal_access_token[expires_at]": expires_at, | |
"personal_access_token[name]": name, | |
"personal_access_token[scopes][]": "api", | |
"utf8": "✓" | |
} | |
data.update(csrf) | |
r = requests.post(pat_route, data=data, cookies=cookies) | |
soup = BeautifulSoup(r.text, "lxml") | |
token = soup.find('input', id='created-personal-access-token').get('value') | |
return token | |
def main(): | |
csrf1, cookies1 = obtain_csrf_token() | |
print("root", csrf1, cookies1) | |
csrf2, cookies2 = sign_in(csrf1, cookies1) | |
print("sign_in", csrf2, cookies2) | |
name = sys.argv[1] | |
expires_at = sys.argv[2] | |
token = obtain_personal_access_token(name, expires_at, csrf2, cookies2) | |
print(token) | |
if __name__ == "__main__": | |
main() |
@vitalyisaev2 please may you add a license to this snippet (perhaps MIT)?
@colin-nolan sorry, I missed your comment, it seems like there is no interface to add license to snippets, so please use it as a public domain.
Hey! I'm trying to implement the same in NodeJS. Can you tell me if the 'requests' is following redirects?
@GCSBOSS as far as I know, yes, it follows redirects by default: https://2.python-requests.org//en/latest/user/quickstart/#redirection-and-history
awesome work, i implement this in a go version https://gist.github.com/liu316484231/0e02025cb507788b1d3be351006e599a
@liu316484231 good job!
Still working in 12.6.2 Enterprise Edition. Thanks so much for this 🙏
@cawwot thanks for clarification!
Still working in 13.4.4 Enterprise Edition, great job !
@Sinayn34 thanks for this information! I've update list of tested versions.
It's surprising that Gitlab was not able to implement this feature for at least 3 major releases.
Hello,
I just tested the code with Python 3.8.3 and 3.9.1, GitLab Enterprise Edition version 12.7.5, and it doesn't not work for me. I get an AttributeError:
data = {param.get("content"): token.get("content")} AttributeError: 'NoneType' object has no attribute 'get'
Do you have an idea of where it comes from?
Thank you much for your excellent work.
Good day to you.
I experienced the same issue. I extended this script with an authenticity_token and a change in the url.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Import Modules
import os
import sys
import requests
import argparse
from urllib.parse import urljoin
from bs4 import BeautifulSoup
# Variables
endpoint = os.environ['GITLAB_URL']
if 'ssh://' in endpoint:
endpoint = "http://" + endpoint.split("@")[1]
login = os.environ['GITLAB_ADMIN_USER']
password = os.environ['GITLAB_ADMIN_PASSWD']
scopes = {'personal_access_token[scopes][]': [
'api', 'sudo', 'read_user', 'read_repository']}
root_route = urljoin(endpoint, "/")
sign_in_route = urljoin(endpoint, "/users/sign_in")
pat_route = urljoin(endpoint, "/-/profile/personal_access_tokens")
# Methods
def find_csrf_token(text):
soup = BeautifulSoup(text, "lxml")
token = soup.find(attrs={"name": "csrf-token"})
param = soup.find(attrs={"name": "csrf-param"})
data = {param.get("content"): token.get("content")}
return data
def obtain_csrf_token():
r = requests.get(root_route)
token = find_csrf_token(r.text)
return token, r.cookies
def obtain_authenticity_token(cookies):
r = requests.get(pat_route, cookies=cookies)
soup = BeautifulSoup(r.text, "lxml")
token = soup.find('input', attrs={'name':'authenticity_token', 'type':'hidden'}).get('value')
return token
def sign_in(csrf, cookies):
data = {
"user[login]": login,
"user[password]": password,
"user[remember_me]": 0,
"utf8": "✓"
}
data.update(csrf)
r = requests.post(sign_in_route, data=data, cookies=cookies)
token = find_csrf_token(r.text)
return token, r.history[0].cookies
def obtain_personal_access_token(name, expires_at, csrf, cookies, authenticity_token):
data = {
"personal_access_token[expires_at]": expires_at,
"personal_access_token[name]": name,
"authenticity_token": authenticity_token,
"utf8": "✓"
}
data.update(scopes)
data.update(csrf)
r = requests.post(pat_route, data=data, cookies=cookies)
soup = BeautifulSoup(r.text, "lxml")
token = soup.find('input', id='created-personal-access-token').get('value')
return token
def main():
csrf1, cookies1 = obtain_csrf_token()
csrf2, cookies2 = sign_in(csrf1, cookies1)
name = sys.argv[1]
expires_at = sys.argv[2]
authenticity_token = obtain_authenticity_token(cookies2)
token = obtain_personal_access_token(name, expires_at, csrf2, cookies2, authenticity_token)
print(token)
if __name__ == "__main__":
main()
I still have the same error, @MiVents
data = {param.get("content"): token.get("content")} AttributeError: 'NoneType object has no attribute' get'
Hi guys, thank you for discussion. I see that the problem is still relevant in more recent versions of Gitlab, so I decided to create a full-fledged repository to give developers opportunity to contribute and maintain newer versions of this script. I think that we need to develop a little library that will provide simple API to obtain tokens.
https://github.com/vitalyisaev2/gitlab_token
If anybody wants to be a maintainer, please drop me a line.
I have dockerized this script in an alpine based container after adding all available
scopes
. Maybe someone finds it useful.