Compare commits

...

5 Commits

8 changed files with 161 additions and 2 deletions

View File

@ -9,7 +9,7 @@ endef
default: ## Run app
$(call run_migrations)
. ./venv/bin/activate && python manage.py runserver
. ./venv/bin/activate && python manage.py runserver 0.0.0.0:7000
coverage: ## Generate test coverage report
. ./venv/bin/activate && coverage run manage.py test

View File

@ -67,6 +67,10 @@ HOSTEA = {
# Repository dedicated for handling support
# ref: https://gitea.hostea.org/Hostea/july-mvp/issues/17
"SUPPORT_REPOSITORY": "support",
"WOODPECKER": {
"HOST": "https://woodpecker.hostea.org", # meta Woodpecker CI instance
"TOKEN": "woodpecker_ci_token", # WOodpecker authentication token
},
},
"INFRA": {
"HOSTEA_REPO": {

View File

@ -180,6 +180,10 @@ HOSTEA = {
# Repository dedicated for handling support
# ref: https://gitea.hostea.org/Hostea/july-mvp/issues/17
"SUPPORT_REPOSITORY": "support",
"WOODPECKER": {
"HOST": "https://woodpecker.hostea.org", # meta Woodpecker CI instance
"TOKEN": "woodpecker_ci_token", # WOodpecker authentication token
},
},
"INFRA": {
"HOSTEA_REPO": {

View File

@ -39,3 +39,6 @@ services:
#ports:
# - "10025:10025"
# - "1080:1080"
volumes:
woodpecker-server-data:

View File

@ -0,0 +1,98 @@
# Copyright © 2022 Aravinth Manivannan <realaravinth@batsense.net>
# Copyright © 2022 enough.community https://lab.enough.community/main/infrastructure/-/blob/master/AUTHORS
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from urillib.parse import urlparse, urlunparse
from django.conf import settings
class CI:
def __init__(self):
self.host = settings.HOSTEA["META"]["WOODPECKER"]["HOST"]
# checking if woodpecker host is URL
_ = urlparse(self.host)
token = settings.HOSTEA["META"]["WOODPECKER"]["TOKEN"]
self.auth_header = {"Authorization": f"Bearer {token}"}
self.w = requests.Session()
self.w.url = f"https://{self.host}"
r = self.w.get(self.w.url + "/authorize", allow_redirects=False)
location = self.gitea_browser.confirm_oauth(
r.headers["Location"], f"https://{self.hostname}/authorize"
)
r = self.w.get(location, allow_redirects=False)
r.raise_for_status()
#
# Woodpecker CSRF
#
r = self.w.get(self.w.url + "/web-config.js", allow_redirects=False)
r.raise_for_status()
csrf = re.findall('window.WOODPECKER_CSRF = "(.*?)"', r.text)[0]
#
# Woodpecker token
#
r = self.w.post(
self.w.url + "/api/user/token",
headers={"X-CSRF-TOKEN": csrf},
allow_redirects=False,
)
r.raise_for_status()
self.token = r.text
self.w.headers = {"Authorization": f"Bearer {self.token}"}
def confirm_oauth(self, url, redirect):
logger.info(f"confirm oauth {url} redirect {redirect}")
r = self.g.get(url, allow_redirects=False)
r.raise_for_status()
if r.status_code == 200:
soup = BeautifulSoup(r.text, "html.parser")
data = {
"redirect_uri": redirect,
}
for input in soup.select(
'form[action="/login/oauth/grant"] input[type="hidden"]'
):
if (
input.get("name") is None
or input.get("value") is None
or input.get("value") == ""
):
continue
logger.info(f"collected hidden input {input['name']} {input['value']}")
data[input["name"]] = input["value"]
assert len(data) > 1, f"{data} has only one field, more are expected"
r = self.g.post(
self.g.url + "/login/oauth/grant", data=data, allow_redirects=False
)
r.raise_for_status()
logger.info("oauth confirmed")
elif r.status_code == 302:
logger.info("no confirmation required")
location = r.headers["Location"]
logger.info(f"going back to {location}")
assert location.startswith(redirect)
return location
def _build_list_url(self):
parsed = urlparse(self.host)
list_builds = "/api/repos/Hostea/dashboard/builds"
return (urlunparse((parsed.scheme, parsed.netloc, list_builds, "", "", "")),)
def logs(self):
resp = request.get(self._build_list_url(), headers=self.auth_header)
print(resp.json())

View File

@ -26,6 +26,7 @@ class Gitea:
self.create_repository()
self.install_sso()
self.add_deploy_key()
self.new_oauth2_app()
def __add_credentials_parser(self, parser):
group = parser.add_argument_group("credentials", "User credentials")
@ -121,6 +122,33 @@ class Gitea:
self.install_sso_parser.set_defaults(func=run)
def new_oauth2_app(self):
def run(args, c: Session):
gitea = gitea_from_args(args, c=c)
# checks if user exists
gitea.login()
creds = gitea.add_oauth_application(
name=args.app_name,
redirect_urls=[args.redirect_uri],
)
print(f"client_id: {creds[0]}client_secret: \n{creds[1]}")
self.install_oauth2_app = self.subparser.add_parser(
name="new_oauth2_app",
description="Create new oauth2 app on Gitea",
help="Create new oauth2 app on Gitea",
)
self.__add_credentials_parser(self.install_oauth2_app)
self.install_oauth2_app.add_argument(
"app_name", type=str, help="(Human readable)Name of the OAuth app"
)
self.install_oauth2_app.add_argument(
"redirect_uri",
type=str,
help="Redirect URI of the app",
)
self.install_oauth2_app.set_defaults(func=run)
def add_deploy_key(self):
def run(args, c: Session):
gitea = gitea_from_args(args, c=c)

View File

@ -297,6 +297,15 @@ class Gitea:
resp = self.c.post(url, json=payload)
assert resp.status_code == 201
def add_oauth_application(self, name: str, redirect_urls: [str]):
url = self.get_api_uri(f"/api/v1/user/applicaitons/oauth2")
payload = {"name": name, "redirect_urls": redirect_urls}
resp = self.c.post(url, json=payload)
print(f"new oauth status code: {resp.status_code}")
assert resp.status_code == 201
data = resp.json()
return (data["client_id"], data["client_secret"])
class ParseSSOLogin(HTMLParser):
url: str = None

View File

@ -13,13 +13,15 @@ then
MAILDEV_URL="http://smtp:1080"
GITEA_URL="http://gitea:3000"
GITEA_SSH_URL="ssh://git@gitea:22"
WOODPECKER_URL="http://woodpecker:8000"
else
MAILDEV_URL="http://localhost:1080"
GITEA_URL="http://localhost:3000"
GITEA_SSH_URL="ssh://git@localhost:22"
WOODPECKER_URL="http://localhost:8000"
fi
readonly DASHBOARD_URL="http://localhost:8000"
readonly DASHBOARD_URL="http://localhost:7000"
readonly DASHBOARD_OIDC_DISCOVERY_URL="$DASHBOARD_URL/o/.well-known/openid-configuration/"
@ -36,6 +38,10 @@ readonly GITEA_HOSTEA_SSO_NAME=hostea-sso
readonly GITEA_OIDC_CALLBACK="$GITEA_URL/user/oauth2/$GITEA_HOSTEA_SSO_NAME/callback"
readonly GITEA_HOSTEA_FLEET_DEPLOY_KEY="$(realpath tests/fleet-deploy-key.pub)"
readonly GITEA_HOSTEA_FLEET_DEPLOY_KEY_PRIVATE="$(realpath tests/fleet-deploy-key)"
readonly GITEA_WOODPECKER_OAUTH_NAME="woodpecker"
readonly WOODPECKER_REDIRECT_URL="$WOODPECKER_URL/authorize"
readonly GITEA_HOSTEA_USERNAME=hostea
readonly GITEA_HOSTEA_PASSWORD=supercomplicatedpassword
@ -89,6 +95,13 @@ gitea_root(){
$GITEA_ROOT_USERNAME $GITEA_ROOT_PASSOWRD \
$GITEA_ROOT_EMAIL \
$GITEA_URL
python -m integration \
gitea new_oauth2_app \
$GITEA_ROOT_USERNAME $GITEA_ROOT_PASSOWRD \
$GITEA_ROOT_EMAIL \
$GITEA_URL \
$GITEA_WOODPECKER_OAUTH_NAME \
$WOODPECKER_REDIRECT_URL
# python -m integration \
# gitea install_sso \
# $GITEA_ROOT_USERNAME $GITEA_ROOT_PASSOWRD \