Skip to content

Commit

Permalink
Merge pull request #140 from billpittman/feature/validator/uri
Browse files Browse the repository at this point in the history
UUri: Validator first check-in
  • Loading branch information
gregmedd authored Jun 7, 2024
2 parents 7b494f3 + a388d17 commit 7a6f9fc
Show file tree
Hide file tree
Showing 3 changed files with 652 additions and 22 deletions.
35 changes: 18 additions & 17 deletions include/up-cpp/datamodel/validator/UUri.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ using ValidationResult = std::tuple<bool, std::optional<Reason>>;
/// A UUri is valid if:
///
/// * It is not empty
/// * It is reserved (or one of the fields is reserved)
/// * it is valid for at least one of:
/// * isValidRpcMethod()
/// * isValidRpcResponse()
Expand All @@ -64,13 +63,13 @@ using ValidationResult = std::tuple<bool, std::optional<Reason>>;

/// @brief Checks if UUri is valid for invoking an RPC method
///
/// The UUri must not be blank/reserved, no field can be a wildcard, and
/// The UUri must not be blank, no field can be a wildcard, and
/// resource_id must be in the range [0x0001, 0x7FFF].
[[nodiscard]] ValidationResult isValidRpcMethod(const v1::UUri&);

/// @brief Checks if UUri is a valid sink for responding to an RPC request.
///
/// The UUri must not be blank/reserved, no field can be a wildcard, and
/// The UUri must not be blank, no field can be a wildcard, and
/// resource_id must be 0.
[[nodiscard]] ValidationResult isValidRpcResponse(const v1::UUri&);

Expand All @@ -85,14 +84,20 @@ using ValidationResult = std::tuple<bool, std::optional<Reason>>;
/// and sink for sending notifications, OR as a sink for receiving
/// notifications.
///
/// The UUri must not be blank/reserved, no field can be a wildcard, and
/// The UUri must not be blank, no field can be a wildcard, and
/// resource_id must be in the range [0x8000, 0xFFFE].
[[nodiscard]] ValidationResult isValidTopic(const v1::UUri&);
[[nodiscard]] ValidationResult isValidPublishTopic(const v1::UUri&);

/// @brief Checks if UUri is valid for notification message.
///
/// The UUri must not be blank, no field can be a wildcard, and
/// resource_id must be in the range [0x8000, 0xFFFE].
[[nodiscard]] ValidationResult isValidNotification(const v1::UUri&);

/// @brief Checks if UUri is valid as a subscription to a published topic or
/// as a source filter when subscribing to a notification.
///
/// The UUri must not be blank/reserved, and resource_id, if not a wildcard,
/// The UUri must not be blank, and resource_id, if not a wildcard,
/// must be in the range [0x8000, 0xFFFE].
[[nodiscard]] ValidationResult isValidSubscription(const v1::UUri&);

Expand All @@ -105,21 +110,17 @@ using ValidationResult = std::tuple<bool, std::optional<Reason>>;
/// * The uE ID is 0
/// * The uE major version is 0
/// * The resource ID is 0
[[nodiscard]] ValidationResult isEmpty(const v1::UUri& uri);

/// @brief Checks if a URI contains an *always* reserved value.
///
/// @note uE Service ID 0 is reserved *for uSubscription*, and is not reserved
/// in all cases. As such, it is not checked for here.
///
/// A UUri is considered reserved if either uE major version is 0 or resource
/// ID is 0.
[[nodiscard]] ValidationResult isReserved(const v1::UUri& uri);
[[nodiscard]] ValidationResult isEmpty(const v1::UUri&);

/// @brief Checks if a UUri is local
///
/// This is just a check for a zero-length authority name string.
[[nodiscard]] bool isLocal(const v1::UUri& uri);
[[nodiscard]] bool isLocal(const v1::UUri&);

/// @brief Checks if a UUri uses wildcards
///
/// Checks for all types of wildcards, returns true if any are found.
[[nodiscard]] bool uses_wildcards(const v1::UUri&);

/// @brief This exception indicates that a UUri object was provided that
/// did not contain valid UUri data.
Expand Down
180 changes: 180 additions & 0 deletions src/datamodel/validator/UUri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,183 @@
// SPDX-License-Identifier: Apache-2.0

#include "up-cpp/datamodel/validator/UUri.h"

namespace uprotocol::datamodel::validator::uri {

using namespace uprotocol;

std::string_view message(Reason reason) {
switch (reason) {
case Reason::EMPTY:
return "The URI authority_name is empty or whitespace.";
case Reason::RESERVED_VERSION:
return "The version is 0 (reserved).";
case Reason::RESERVED_RESOURCE:
return "The resource is 0 for non-RPC-response URIs.";
case Reason::DISALLOWED_WILDCARD:
return "The URI contains a wildcard in one or more fields that is "
"not allowed for this form.";
case Reason::BAD_RESOURCE_ID:
return "The resource ID is not in the allowed range for this URI "
"form.";
default:
return "Unknown reason.";
}
}

bool uses_wildcards(const v1::UUri& uuri) {
if (uuri.authority_name().find("*") != std::string::npos) {
return true;
}
if ((uuri.ue_id() & 0xFFFF) == 0xFFFF) { // service ID
return true;
}
if ((uuri.ue_id() & 0xFFFF0000) == 0) { // service instance ID
return true;
}
if (uuri.ue_version_major() == 0xFF) {
return true;
}
if (uuri.resource_id() == 0xFFFF) {
return true;
}
return false;
}

ValidationResult isValid(const v1::UUri& uuri) {
{
auto [valid, reason] = isValidRpcMethod(uuri);
if (valid) {
return {true, std::nullopt};
}
}

{
auto [valid, reason] = isValidRpcResponse(uuri);
if (valid) {
return {true, std::nullopt};
}
}

{
auto [valid, reason] = isValidPublishTopic(uuri);
if (valid) {
return {true, std::nullopt};
}
}

return isValidNotification(uuri);
}

ValidationResult isValidRpcMethod(const v1::UUri& uuri) {
if (uuri.authority_name().empty()) {
return {false, Reason::EMPTY};
}

// disallow wildcards
if (uses_wildcards(uuri)) {
return {false, Reason::DISALLOWED_WILDCARD};
}

// check resource ID [0x0001, 0x7FFF]
if (uuri.resource_id() == 0 || uuri.resource_id() > 0x7FFF) {
return {false, Reason::BAD_RESOURCE_ID};
}

return {true, std::nullopt};
}

ValidationResult isValidRpcResponse(const v1::UUri& uuri) {
if (uuri.authority_name().empty()) {
return {false, Reason::EMPTY};
}

// disallow wildcards
if (uses_wildcards(uuri)) {
return {false, Reason::DISALLOWED_WILDCARD};
}

if (uuri.resource_id() != 0) {
return {false, Reason::BAD_RESOURCE_ID};
}
return {true, std::nullopt};
}

ValidationResult isValidDefaultSource(const v1::UUri& uuri) {
return isValidRpcResponse(uuri);
}

ValidationResult isValidPublishTopic(const v1::UUri& uuri) {
if (uuri.authority_name().empty()) {
return {false, Reason::EMPTY};
}

// disallow wildcards
if (uses_wildcards(uuri)) {
return {false, Reason::DISALLOWED_WILDCARD};
}

if ((uuri.resource_id() < 0x8000) || (uuri.resource_id() > 0xFFFF)) {
return {false, Reason::BAD_RESOURCE_ID};
}

return {true, std::nullopt};
}

ValidationResult isValidNotification(const v1::UUri& uuri) {
if (uuri.authority_name().empty()) {
return {false, Reason::EMPTY};
}

// disallow wildcards
if (uses_wildcards(uuri)) {
return {false, Reason::DISALLOWED_WILDCARD};
}

if ((uuri.resource_id() < 0x8000) && (uuri.resource_id() != 0)) {
return {false, Reason::BAD_RESOURCE_ID};
}

if (uuri.resource_id() > 0xFFFF) {
return {false, Reason::BAD_RESOURCE_ID};
}

return {true, std::nullopt};
}

ValidationResult isValidSubscription(const v1::UUri& uuri) {
if (uuri.authority_name().empty()) {
return {false, Reason::EMPTY};
}

if (uuri.resource_id() < 0x8000 || uuri.resource_id() > 0xFFFF) {
return {false, Reason::BAD_RESOURCE_ID};
}

return {true, std::nullopt};
}

ValidationResult isEmpty(const v1::UUri& uuri) {
if (!std::all_of(uuri.authority_name().begin(), uuri.authority_name().end(),
isspace)) {
return {false, Reason::EMPTY};
}

if (uuri.ue_id() != 0) {
return {false, Reason::RESERVED_RESOURCE};
}

if (uuri.ue_version_major() != 0) {
return {false, Reason::RESERVED_VERSION};
}

if (uuri.resource_id() != 0) {
return {false, Reason::BAD_RESOURCE_ID};
}

return {true, std::nullopt};
}

bool isLocal(const v1::UUri& uuri) { return (uuri.authority_name().empty()); }

} // namespace uprotocol::datamodel::validator::uri
Loading

0 comments on commit 7a6f9fc

Please sign in to comment.