[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:
Alexandre Aubin 2017-04-16 16:47:51 +02:00 committed by GitHub
parent 674d639530
commit f646fdf272
5 changed files with 195 additions and 0 deletions

View file

@ -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.
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()
# info:
@ -563,6 +578,7 @@ app:
checkurl:
action_help: Check availability of a web path
api: GET /tools/checkurl
deprecated: True
configuration:
authenticate: all
authenticator: ldap-anonymous
@ -573,6 +589,22 @@ app:
full: --app
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()
initdb:
action_help: Create database and initialize it with optionnal attached script

View file

@ -4,6 +4,7 @@
"admin_password_change_failed": "Unable to change password",
"admin_password_changed": "The administration password has been changed",
"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_invalid": "Invalid value for argument '{name:s}': {error:s}",
"app_argument_required": "Argument '{name:s}' is required",
@ -13,6 +14,7 @@
"app_install_files_invalid": "Invalid installation files",
"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_unavailable": "This url is not available or conflicts with an already installed app",
"app_manifest_invalid": "Invalid app manifest",
"app_no_upgrade": "No app to upgrade",
"app_not_correctly_installed": "{app:s} seems to be incorrectly installed",
@ -119,6 +121,7 @@
"hook_name_unknown": "Unknown hook name '{name:s}'",
"installation_complete": "Installation complete",
"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",
"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",

View file

@ -966,6 +966,42 @@ def app_checkport(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):
"""
Check availability of a web path

View file

@ -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)
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):
"""Retrieve the public IP address from ip.yunohost.org"""
if protocol == 4:
@ -300,3 +339,21 @@ def _get_maindomain():
def _set_maindomain(domain):
with open('/etc/yunohost/current_host', 'w') as f:
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

View 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"))