diff --git a/CHANGELOG.md b/CHANGELOG.md index 84d260853fe..1f3684d0192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ This file is a running track of new features and fixes to each version of the pa This project follows [Semantic Versioning](http://semver.org) guidelines. +## v2.0.2-beta +### Fixed +* FQDN validator for the hostname field when adding a new node + ## v2.0.1-beta ### Fixed * Problem where validation errors for the SSH key wouldn't show up diff --git a/app/Http/Requests/Admin/Nodes/StoreNodeRequest.php b/app/Http/Requests/Admin/Nodes/StoreNodeRequest.php index 8252e65fcd9..9c48e3b15f5 100644 --- a/app/Http/Requests/Admin/Nodes/StoreNodeRequest.php +++ b/app/Http/Requests/Admin/Nodes/StoreNodeRequest.php @@ -3,6 +3,7 @@ namespace Convoy\Http\Requests\Admin\Nodes; use Convoy\Models\Node; +use Convoy\Rules\Network\Fqdn; use Illuminate\Foundation\Http\FormRequest; class StoreNodeRequest extends FormRequest @@ -24,6 +25,10 @@ public function authorize() */ public function rules() { - return Node::getRules(); + $rules = Node::getRules(); + + $rules['hostname'][] = Fqdn::make(); + + return $rules; } } diff --git a/app/Models/Node.php b/app/Models/Node.php index 1fa98603e91..d615d2c5315 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -15,7 +15,7 @@ class Node extends Model public static $validationRules = [ 'name' => 'required|string|max:191', 'cluster' => 'required|string|max:191', - 'hostname' => ['required', 'regex:/^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$/'], + 'hostname' => 'required|string', 'token_id' => 'required|string', 'secret' => 'required|string', 'port' => 'required|integer', diff --git a/app/Rules/Network/Fqdn.php b/app/Rules/Network/Fqdn.php new file mode 100644 index 00000000000..e6043d455ee --- /dev/null +++ b/app/Rules/Network/Fqdn.php @@ -0,0 +1,103 @@ + and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +namespace Convoy\Rules\Network; + +use Illuminate\Support\Arr; +use Illuminate\Contracts\Validation\Rule; +use Illuminate\Contracts\Validation\DataAwareRule; + +class Fqdn implements Rule, DataAwareRule +{ + protected array $data = []; + protected string $message = ''; + protected ?string $schemeField = null; + + /** + * @param array $data + */ + public function setData($data): self + { + $this->data = $data; + + return $this; + } + + /** + * Validates that the value provided resolves to an IP address. If a scheme is + * specified when this rule is created additional checks will be applied. + * + * @param string $attribute + * @param mixed $value + */ + public function passes($attribute, $value): bool + { + if (filter_var($value, FILTER_VALIDATE_IP)) { + // Check if the scheme is set to HTTPS. + // + // Unless someone owns their IP blocks and decides to pay who knows how much for a + // custom SSL cert, IPs will not be able to use HTTPS. This should prevent most + // home users from making this mistake and wondering why their node is not working. + if ($this->schemeField && Arr::get($this->data, $this->schemeField) === 'https') { + $this->message = 'The :attribute must not be an IP address when HTTPS is enabled.'; + + return false; + } + + return true; + } + + // Lookup A and AAAA DNS records for the FQDN. Note, this function will also resolve CNAMEs + // for us automatically, there is no need to manually resolve them here. + // + // The error suppression is intentional, see https://bugs.php.net/bug.php?id=73149 + $records = @dns_get_record($value, DNS_A + DNS_AAAA); + // If no records were returned fall back to trying to resolve the value using the hosts DNS + // resolution. This will not work for IPv6 which is why we prefer to use `dns_get_record` + // first. + if (!empty($records) || filter_var(gethostbyname($value), FILTER_VALIDATE_IP)) { + return true; + } + + $this->message = 'The :attribute could not be resolved to a valid IP address.'; + + return false; + } + + public function message(): string + { + return $this->message; + } + + /** + * Returns a new instance of the rule with a defined scheme set. + */ + public static function make(string $schemeField = null): self + { + return tap(new static(), function ($fqdn) use ($schemeField) { + $fqdn->schemeField = $schemeField; + }); + } +}