diff options
-rw-r--r-- | hscript/user.cc | 24 | ||||
-rw-r--r-- | tests/fixtures/0098-usericon-basic.installfile | 7 | ||||
-rw-r--r-- | tests/fixtures/0099-usericon-without-icon.installfile | 7 | ||||
-rw-r--r-- | tests/fixtures/0100-usericon-unknown-name.installfile | 8 | ||||
-rw-r--r-- | tests/fixtures/0101-usericon-duplicate.installfile | 8 | ||||
-rw-r--r-- | tests/fixtures/0102-usericon-protocols.installfile | 15 | ||||
-rw-r--r-- | tests/fixtures/0103-usericon-gopher.installfile | 7 | ||||
-rw-r--r-- | tests/spec/validator.rb | 39 | ||||
-rw-r--r-- | util/net.hh | 36 |
9 files changed, 149 insertions, 2 deletions
diff --git a/hscript/user.cc b/hscript/user.cc index 6081f55..04258e4 100644 --- a/hscript/user.cc +++ b/hscript/user.cc @@ -17,6 +17,7 @@ #include <sstream> #include <time.h> #include "user.hh" +#include "util/net.hh" #include "util/output.hh" using namespace Horizon::Keys; @@ -264,11 +265,30 @@ bool UserPassphrase::execute(ScriptOptions) const { Key *UserIcon::parseFromData(const std::string &data, int lineno, int *errors, int *warnings) { - return nullptr; + /* REQ: Runner.Validate.usericon.Validity */ + const std::string::size_type sep = data.find_first_of(' '); + if(sep == std::string::npos || data.length() == sep + 1) { + if(errors) *errors += 1; + output_error("installfile:" + std::to_string(lineno), + "usericon: icon is required", + "expected format is: usericon [username] [path|url]"); + return nullptr; + } + + std::string icon_path = data.substr(sep + 1); + if(icon_path[0] != '/' && !is_valid_url(icon_path)) { + if(errors) *errors += 1; + output_error("installfile:" + std::to_string(lineno), + "usericon: path must be absolute path or valid URL"); + return nullptr; + } + + return new UserIcon(lineno, data.substr(0, sep), icon_path); } bool UserIcon::validate(ScriptOptions) const { - return false; + /* TODO XXX: ensure URL is accessible */ + return true; } bool UserIcon::execute(ScriptOptions) const { diff --git a/tests/fixtures/0098-usericon-basic.installfile b/tests/fixtures/0098-usericon-basic.installfile new file mode 100644 index 0000000..8abb1a7 --- /dev/null +++ b/tests/fixtures/0098-usericon-basic.installfile @@ -0,0 +1,7 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +username awilfox +usericon awilfox /usr/share/user-manager/avatars/circles/Cat.png diff --git a/tests/fixtures/0099-usericon-without-icon.installfile b/tests/fixtures/0099-usericon-without-icon.installfile new file mode 100644 index 0000000..bdedf34 --- /dev/null +++ b/tests/fixtures/0099-usericon-without-icon.installfile @@ -0,0 +1,7 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +username awilfox +usericon awilfox diff --git a/tests/fixtures/0100-usericon-unknown-name.installfile b/tests/fixtures/0100-usericon-unknown-name.installfile new file mode 100644 index 0000000..047b8eb --- /dev/null +++ b/tests/fixtures/0100-usericon-unknown-name.installfile @@ -0,0 +1,8 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +username awilfox +# Intentional misspelling +usericon awilcox /usr/share/user-manager/avatars/circles/Cat.png diff --git a/tests/fixtures/0101-usericon-duplicate.installfile b/tests/fixtures/0101-usericon-duplicate.installfile new file mode 100644 index 0000000..b572891 --- /dev/null +++ b/tests/fixtures/0101-usericon-duplicate.installfile @@ -0,0 +1,8 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +username awilfox +usericon awilfox /usr/share/user-manager/avatars/circles/Cat.png +usericon awilfox /usr/share/user-manager/avatars/classic/TV.png diff --git a/tests/fixtures/0102-usericon-protocols.installfile b/tests/fixtures/0102-usericon-protocols.installfile new file mode 100644 index 0000000..246dc94 --- /dev/null +++ b/tests/fixtures/0102-usericon-protocols.installfile @@ -0,0 +1,15 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +username chris +usericon chris http://www.adelielinux.org/ +username kayla +usericon kayla HTTPS://www.adelielinux.org/ +username meg +usericon meg /usr/share/user-manager/avatars/circles/Cat.png +username steph +usericon steph tftp://192.168.1.1/avatar +username amanda +usericon amanda SmB://stuff:pw@10.4.1.1/c$/mypicture diff --git a/tests/fixtures/0103-usericon-gopher.installfile b/tests/fixtures/0103-usericon-gopher.installfile new file mode 100644 index 0000000..4a901b5 --- /dev/null +++ b/tests/fixtures/0103-usericon-gopher.installfile @@ -0,0 +1,7 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +username awilfox +usericon awilfox gopher://bbs.sick.bike/l/upload/b/13.jpe diff --git a/tests/spec/validator.rb b/tests/spec/validator.rb index 76f1c6f..e43e5ed 100644 --- a/tests/spec/validator.rb +++ b/tests/spec/validator.rb @@ -612,6 +612,45 @@ RSpec.describe 'HorizonScript validation', :type => :aruba do expect(last_command_started).to have_output(/warning: .*passphrase/) end end + context "'usericon'" do + # Runner.Validate.usericon. + it "succeeds with a valid icon/account pair" do + use_fixture '0098-usericon-basic.installfile' + run_validate + expect(last_command_started).to have_output(PARSER_SUCCESS) + expect(last_command_started).to have_output(VALIDATOR_SUCCESS) + end + # Runner.Validate.usericon.Validity. + it "requires a path to be provided" do + use_fixture '0099-usericon-without-icon.installfile' + run_validate + expect(last_command_started).to have_output(/error: .*usericon.*required/) + end + # Runner.Validate.usericon.Name. + it "fails with a username that wasn't given" do + use_fixture '0100-usericon-unknown-name.installfile' + run_validate + expect(last_command_started).to have_output(/error: .*usericon.*name/) + end + # Runner.Validate.usericon.Unique. + it "fails with multiple icons associated with a single account" do + use_fixture '0101-usericon-duplicate.installfile' + run_validate + expect(last_command_started).to have_output(/error: .*duplicate.*usericon/) + end + # Runner.Validate.usericon.ValidPath. + it "succeeds with all supported protocols" do + use_fixture '0102-usericon-protocols.installfile' + run_validate + expect(last_command_started).to have_output(PARSER_SUCCESS) + expect(last_command_started).to have_output(VALIDATOR_SUCCESS) + end + it "fails with an unsupported protocol" do + use_fixture '0103-usericon-gopher.installfile' + run_validate + expect(last_command_started).to have_output(/error: .*usericon.*URL/) + end + end end context "package specifications" do # no requirements for these, but I think obvious. diff --git a/util/net.hh b/util/net.hh new file mode 100644 index 0000000..89e5303 --- /dev/null +++ b/util/net.hh @@ -0,0 +1,36 @@ +/* + * net.hh - Miscellaneous networking routines + * util, the utility library for + * Project Horizon + * + * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved. + * This code is licensed under the AGPL 3.0 license, as noted in the + * LICENSE-code file in the root directory of this repository. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#ifndef __HORIZON_NET_HH_ +#define __HORIZON_NET_HH_ + +#include <algorithm> +#include <string> + +/*! Determine if a string starts with a valid, supported protocol + * @param url The URL. + * @returns true if +url+ is a URL for a supported protocol, false otherwise. + */ +static bool is_valid_url(const std::string &url) { + std::string::size_type colon = url.find("://"); + /* If there's no ://, it's definitely not a URL */ + if(colon == std::string::npos) return false; + std::string proto = url.substr(0, colon); + std::transform(proto.cbegin(), proto.cend(), proto.begin(), ::tolower); + if(proto == "http" || proto == "https" || proto == "tftp" || proto == "smb" + || proto == "cifs") { + return true; + } + return false; +} + +#endif /* !__HORIZON_NET_HH */ |