mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
[fix] Split checkurl into two functions : availability + booking (#267)
* Splitting checkurl into two functions, one to check availability, the other for booking * [fix] move import at file's beginning. * Rename bookurl to registerurl * Set registerurl as a PUT request for the api * urlavailable returns a boolean now * Revert moving import to top of file :/ * Have domain and path as separate arguments * Flagging checkurl as deprecated in the actionmap * Adding unit tests for registerurl and related * Using built-in deprectation mechanism of Moulinette * Using - separator in names + moving url-available to domain * Returning directly a bool in url-available
This commit is contained in:
parent
674d639530
commit
f646fdf272
5 changed files with 195 additions and 0 deletions
|
@ -368,6 +368,21 @@ domain:
|
||||||
help: Use the fake/staging Let's Encrypt certification authority. The new certificate won't actually be enabled - it is only intended to test the main steps of the procedure.
|
help: Use the fake/staging Let's Encrypt certification authority. The new certificate won't actually be enabled - it is only intended to test the main steps of the procedure.
|
||||||
action: store_true
|
action: store_true
|
||||||
|
|
||||||
|
### domain_url_available()
|
||||||
|
url-available:
|
||||||
|
action_help: Check availability of a web path
|
||||||
|
api: GET /domain/urlavailable
|
||||||
|
configuration:
|
||||||
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
|
arguments:
|
||||||
|
domain:
|
||||||
|
help: The domain for the web path (e.g. your.domain.tld)
|
||||||
|
extra:
|
||||||
|
pattern: *pattern_domain
|
||||||
|
path:
|
||||||
|
help: The path to check (e.g. /coffee)
|
||||||
|
|
||||||
|
|
||||||
### domain_info()
|
### domain_info()
|
||||||
# info:
|
# info:
|
||||||
|
@ -563,6 +578,7 @@ app:
|
||||||
checkurl:
|
checkurl:
|
||||||
action_help: Check availability of a web path
|
action_help: Check availability of a web path
|
||||||
api: GET /tools/checkurl
|
api: GET /tools/checkurl
|
||||||
|
deprecated: True
|
||||||
configuration:
|
configuration:
|
||||||
authenticate: all
|
authenticate: all
|
||||||
authenticator: ldap-anonymous
|
authenticator: ldap-anonymous
|
||||||
|
@ -573,6 +589,22 @@ app:
|
||||||
full: --app
|
full: --app
|
||||||
help: Write domain & path to app settings for further checks
|
help: Write domain & path to app settings for further checks
|
||||||
|
|
||||||
|
### app_register_url()
|
||||||
|
register-url:
|
||||||
|
action_help: Book/register a web path for a given app
|
||||||
|
api: PUT /tools/registerurl
|
||||||
|
configuration:
|
||||||
|
authenticate: all
|
||||||
|
authenticator: ldap-anonymous
|
||||||
|
arguments:
|
||||||
|
app:
|
||||||
|
help: App which will use the web path
|
||||||
|
domain:
|
||||||
|
help: The domain on which the app should be registered (e.g. your.domain.tld)
|
||||||
|
path:
|
||||||
|
help: The path to be registered (e.g. /coffee)
|
||||||
|
|
||||||
|
|
||||||
### app_initdb()
|
### app_initdb()
|
||||||
initdb:
|
initdb:
|
||||||
action_help: Create database and initialize it with optionnal attached script
|
action_help: Create database and initialize it with optionnal attached script
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"admin_password_change_failed": "Unable to change password",
|
"admin_password_change_failed": "Unable to change password",
|
||||||
"admin_password_changed": "The administration password has been changed",
|
"admin_password_changed": "The administration password has been changed",
|
||||||
"app_already_installed": "{app:s} is already installed",
|
"app_already_installed": "{app:s} is already installed",
|
||||||
|
"app_already_installed_cant_change_url": "This app is already installed. The url cannot be changed just by this function. Look into `app changeurl` if it's available.",
|
||||||
"app_argument_choice_invalid": "Invalid choice for argument '{name:s}', it must be one of {choices:s}",
|
"app_argument_choice_invalid": "Invalid choice for argument '{name:s}', it must be one of {choices:s}",
|
||||||
"app_argument_invalid": "Invalid value for argument '{name:s}': {error:s}",
|
"app_argument_invalid": "Invalid value for argument '{name:s}': {error:s}",
|
||||||
"app_argument_required": "Argument '{name:s}' is required",
|
"app_argument_required": "Argument '{name:s}' is required",
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
"app_install_files_invalid": "Invalid installation files",
|
"app_install_files_invalid": "Invalid installation files",
|
||||||
"app_location_already_used": "An app is already installed in this location",
|
"app_location_already_used": "An app is already installed in this location",
|
||||||
"app_location_install_failed": "Unable to install the app in this location",
|
"app_location_install_failed": "Unable to install the app in this location",
|
||||||
|
"app_location_unavailable": "This url is not available or conflicts with an already installed app",
|
||||||
"app_manifest_invalid": "Invalid app manifest",
|
"app_manifest_invalid": "Invalid app manifest",
|
||||||
"app_no_upgrade": "No app to upgrade",
|
"app_no_upgrade": "No app to upgrade",
|
||||||
"app_not_correctly_installed": "{app:s} seems to be incorrectly installed",
|
"app_not_correctly_installed": "{app:s} seems to be incorrectly installed",
|
||||||
|
@ -119,6 +121,7 @@
|
||||||
"hook_name_unknown": "Unknown hook name '{name:s}'",
|
"hook_name_unknown": "Unknown hook name '{name:s}'",
|
||||||
"installation_complete": "Installation complete",
|
"installation_complete": "Installation complete",
|
||||||
"installation_failed": "Installation failed",
|
"installation_failed": "Installation failed",
|
||||||
|
"invalid_url_format": "Invalid URL format",
|
||||||
"ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it",
|
"ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it",
|
||||||
"iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it",
|
"iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it",
|
||||||
"ldap_initialized": "LDAP has been initialized",
|
"ldap_initialized": "LDAP has been initialized",
|
||||||
|
|
|
@ -966,6 +966,42 @@ def app_checkport(port):
|
||||||
m18n.n('port_unavailable', port=int(port)))
|
m18n.n('port_unavailable', port=int(port)))
|
||||||
|
|
||||||
|
|
||||||
|
def app_register_url(auth, app, domain, path):
|
||||||
|
"""
|
||||||
|
Book/register a web path for a given app
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
app -- App which will use the web path
|
||||||
|
domain -- The domain on which the app should be registered (e.g. your.domain.tld)
|
||||||
|
path -- The path to be registered (e.g. /coffee)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# This line can't be moved on top of file, otherwise it creates an infinite
|
||||||
|
# loop of import with tools.py...
|
||||||
|
from domain import domain_url_available, _normalize_domain_path
|
||||||
|
|
||||||
|
domain, path = _normalize_domain_path(domain, path)
|
||||||
|
|
||||||
|
# We cannot change the url of an app already installed simply by changing
|
||||||
|
# the settings...
|
||||||
|
# FIXME should look into change_url once it's merged
|
||||||
|
|
||||||
|
installed = app in app_list(installed=True, raw=True).keys()
|
||||||
|
if installed:
|
||||||
|
settings = _get_app_settings(app)
|
||||||
|
if "path" in settings.keys() and "domain" in settings.keys():
|
||||||
|
raise MoulinetteError(errno.EINVAL,
|
||||||
|
m18n.n('app_already_installed_cant_change_url'))
|
||||||
|
|
||||||
|
# Check the url is available
|
||||||
|
if not domain_url_available(auth, domain, path):
|
||||||
|
raise MoulinetteError(errno.EINVAL,
|
||||||
|
m18n.n('app_location_unavailable'))
|
||||||
|
|
||||||
|
app_setting(app, 'domain', value=domain)
|
||||||
|
app_setting(app, 'path', value=path)
|
||||||
|
|
||||||
|
|
||||||
def app_checkurl(auth, url, app=None):
|
def app_checkurl(auth, url, app=None):
|
||||||
"""
|
"""
|
||||||
Check availability of a web path
|
Check availability of a web path
|
||||||
|
|
|
@ -275,6 +275,45 @@ def domain_cert_renew(auth, domain_list, force=False, no_checks=False, email=Fal
|
||||||
return yunohost.certificate.certificate_renew(auth, domain_list, force, no_checks, email, staging)
|
return yunohost.certificate.certificate_renew(auth, domain_list, force, no_checks, email, staging)
|
||||||
|
|
||||||
|
|
||||||
|
def domain_url_available(auth, domain, path):
|
||||||
|
"""
|
||||||
|
Check availability of a web path
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
domain -- The domain for the web path (e.g. your.domain.tld)
|
||||||
|
path -- The path to check (e.g. /coffee)
|
||||||
|
"""
|
||||||
|
|
||||||
|
domain, path = _normalize_domain_path(domain, path)
|
||||||
|
|
||||||
|
# Abort if domain is unknown
|
||||||
|
if domain not in domain_list(auth)['domains']:
|
||||||
|
raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown'))
|
||||||
|
|
||||||
|
# This import cannot be put on top of file because it would create a
|
||||||
|
# recursive import...
|
||||||
|
from yunohost.app import app_map
|
||||||
|
|
||||||
|
# Fetch apps map
|
||||||
|
apps_map = app_map(raw=True)
|
||||||
|
|
||||||
|
# Loop through all apps to check if path is taken by one of them
|
||||||
|
available = True
|
||||||
|
if domain in apps_map:
|
||||||
|
# Loop through apps
|
||||||
|
for p, a in apps_map[domain].items():
|
||||||
|
if path == p:
|
||||||
|
available = False
|
||||||
|
break
|
||||||
|
# We also don't want conflicts with other apps starting with
|
||||||
|
# same name
|
||||||
|
elif path.startswith(p) or p.startswith(path):
|
||||||
|
available = False
|
||||||
|
break
|
||||||
|
|
||||||
|
return available
|
||||||
|
|
||||||
|
|
||||||
def get_public_ip(protocol=4):
|
def get_public_ip(protocol=4):
|
||||||
"""Retrieve the public IP address from ip.yunohost.org"""
|
"""Retrieve the public IP address from ip.yunohost.org"""
|
||||||
if protocol == 4:
|
if protocol == 4:
|
||||||
|
@ -300,3 +339,21 @@ def _get_maindomain():
|
||||||
def _set_maindomain(domain):
|
def _set_maindomain(domain):
|
||||||
with open('/etc/yunohost/current_host', 'w') as f:
|
with open('/etc/yunohost/current_host', 'w') as f:
|
||||||
f.write(domain)
|
f.write(domain)
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_domain_path(domain, path):
|
||||||
|
|
||||||
|
# We want url to be of the format :
|
||||||
|
# some.domain.tld/foo
|
||||||
|
|
||||||
|
# Remove http/https prefix if it's there
|
||||||
|
if domain.startswith("https://"):
|
||||||
|
domain = domain[len("https://"):]
|
||||||
|
elif domain.startswith("http://"):
|
||||||
|
domain = domain[len("http://"):]
|
||||||
|
|
||||||
|
# Remove trailing slashes
|
||||||
|
domain = domain.rstrip("/")
|
||||||
|
path = "/" + path.strip("/")
|
||||||
|
|
||||||
|
return domain, path
|
||||||
|
|
67
src/yunohost/tests/test_appurl.py
Normal file
67
src/yunohost/tests/test_appurl.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from moulinette.core import MoulinetteError, init_authenticator
|
||||||
|
|
||||||
|
from yunohost.app import app_install, app_remove
|
||||||
|
from yunohost.domain import _get_maindomain, domain_url_available, _normalize_domain_path
|
||||||
|
|
||||||
|
# Instantiate LDAP Authenticator
|
||||||
|
auth_identifier = ('ldap', 'ldap-anonymous')
|
||||||
|
auth_parameters = {'uri': 'ldap://localhost:389', 'base_dn': 'dc=yunohost,dc=org'}
|
||||||
|
auth = init_authenticator(auth_identifier, auth_parameters)
|
||||||
|
|
||||||
|
|
||||||
|
# Get main domain
|
||||||
|
maindomain = _get_maindomain()
|
||||||
|
|
||||||
|
|
||||||
|
def setup_function(function):
|
||||||
|
|
||||||
|
try:
|
||||||
|
app_remove(auth, "register_url_app")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def teardown_function(function):
|
||||||
|
|
||||||
|
try:
|
||||||
|
app_remove(auth, "register_url_app")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_domain_path():
|
||||||
|
|
||||||
|
assert _normalize_domain_path("https://yolo.swag/", "macnuggets") == ("yolo.swag", "/macnuggets")
|
||||||
|
assert _normalize_domain_path("http://yolo.swag", "/macnuggets/") == ("yolo.swag", "/macnuggets")
|
||||||
|
assert _normalize_domain_path("yolo.swag/", "macnuggets/") == ("yolo.swag", "/macnuggets")
|
||||||
|
|
||||||
|
|
||||||
|
def test_urlavailable():
|
||||||
|
|
||||||
|
# Except the maindomain/macnuggets to be available
|
||||||
|
assert domain_url_available(auth, maindomain, "/macnuggets")
|
||||||
|
|
||||||
|
# We don't know the domain yolo.swag
|
||||||
|
with pytest.raises(MoulinetteError):
|
||||||
|
assert domain_url_available(auth, "yolo.swag", "/macnuggets")
|
||||||
|
|
||||||
|
|
||||||
|
def test_registerurl():
|
||||||
|
|
||||||
|
app_install(auth, "./tests/apps/register_url_app_ynh",
|
||||||
|
args="domain=%s&path=%s" % (maindomain, "/urlregisterapp"))
|
||||||
|
|
||||||
|
assert not domain_url_available(auth, maindomain, "/urlregisterapp")
|
||||||
|
|
||||||
|
# Try installing at same location
|
||||||
|
with pytest.raises(MoulinetteError):
|
||||||
|
app_install(auth, "./tests/apps/register_url_app_ynh",
|
||||||
|
args="domain=%s&path=%s" % (maindomain, "/urlregisterapp"))
|
||||||
|
|
||||||
|
|
||||||
|
def test_registerurl_baddomain():
|
||||||
|
|
||||||
|
with pytest.raises(MoulinetteError):
|
||||||
|
app_install(auth, "./tests/apps/register_url_app_ynh",
|
||||||
|
args="domain=%s&path=%s" % ("yolo.swag", "/urlregisterapp"))
|
Loading…
Add table
Reference in a new issue