KiCad PCB EDA Suite
Loading...
Searching...
No Matches
remote_provider_metadata.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
21
22#include <set>
23
25#include <paths.h>
27#include <wx_filename.h>
28#include <wx/intl.h>
29
30
32{
33 wxFileName schemaFile( PATHS::GetStockDataPath( true ),
34 wxS( "kicad-remote-provider-metadata-v1.schema.json" ) );
35 schemaFile.AppendDir( wxS( "schemas" ) );
36 schemaFile.Normalize( FN_NORMALIZE_FLAGS );
37 return schemaFile;
38}
39
40
41std::optional<REMOTE_PROVIDER_METADATA> REMOTE_PROVIDER_METADATA::FromJson(
42 const nlohmann::json& aJson, wxString& aError )
43{
44 return FromJson( aJson, DefaultSchemaPath(), aError );
45}
46
47
48std::optional<REMOTE_PROVIDER_METADATA> REMOTE_PROVIDER_METADATA::FromJson(
49 const nlohmann::json& aJson, const wxFileName& aSchemaFile, wxString& aError )
50{
51 if( !aSchemaFile.IsFileReadable() )
52 {
53 aError = wxString::Format( _( "Remote provider metadata schema '%s' is not readable." ),
54 aSchemaFile.GetFullPath() );
55 return std::nullopt;
56 }
57
59 JSON_SCHEMA_VALIDATOR validator( aSchemaFile );
60 validator.Validate( aJson, handler );
61
62 if( handler.HasErrors() )
63 {
64 aError = wxString::Format( _( "Remote provider metadata failed schema validation: %s" ),
65 handler.FirstError() );
66 return std::nullopt;
67 }
68
70
71 try
72 {
73 metadata.provider_name =
74 wxString::FromUTF8( aJson.at( "provider_name" ).get_ref<const std::string&>().c_str() );
75 metadata.provider_version =
76 wxString::FromUTF8( aJson.at( "provider_version" ).get_ref<const std::string&>().c_str() );
77 metadata.api_base_url =
78 wxString::FromUTF8( aJson.at( "api_base_url" ).get_ref<const std::string&>().c_str() );
79 metadata.panel_url = RemoteProviderJsonString( aJson, "panel_url" );
80 metadata.session_bootstrap_url = RemoteProviderJsonString( aJson, "session_bootstrap_url" );
81 metadata.max_download_bytes = aJson.at( "max_download_bytes" ).get<long long>();
82 metadata.allow_insecure_localhost = aJson.value( "allow_insecure_localhost", false );
83 metadata.documentation_url = RemoteProviderJsonString( aJson, "documentation_url" );
84 metadata.terms_url = RemoteProviderJsonString( aJson, "terms_url" );
85 metadata.privacy_url = RemoteProviderJsonString( aJson, "privacy_url" );
86
87 for( const nlohmann::json& assetType : aJson.at( "supported_asset_types" ) )
88 metadata.supported_asset_types.emplace_back( wxString::FromUTF8(
89 assetType.get_ref<const std::string&>().c_str() ) );
90
91 const nlohmann::json& auth = aJson.at( "auth" );
92 wxString authType = RemoteProviderJsonString( auth, "type" );
93
94 if( authType.IsSameAs( wxS( "oauth2" ), false ) )
95 {
97 metadata.auth.metadata_url = RemoteProviderJsonString( auth, "metadata_url" );
98 metadata.auth.client_id = RemoteProviderJsonString( auth, "client_id" );
99
100 if( auth.contains( "scopes" ) )
101 {
102 for( const nlohmann::json& scope : auth.at( "scopes" ) )
103 metadata.auth.scopes.emplace_back(
104 wxString::FromUTF8( scope.get_ref<const std::string&>().c_str() ) );
105 }
106 }
107 else
108 {
110 }
111
112 const nlohmann::json& capabilities = aJson.at( "capabilities" );
113 static const std::set<std::string> supportedCapabilities = {
114 "web_ui_v1",
115 "parts_v1",
116 "direct_downloads_v1",
117 "inline_payloads_v1"
118 };
119
120 for( const auto& [name, value] : capabilities.items() )
121 {
122 if( !supportedCapabilities.count( name ) )
123 {
124 aError = wxString::Format( _( "Unsupported provider capability '%s'." ),
125 wxString::FromUTF8( name.c_str() ) );
126 return std::nullopt;
127 }
128
129 if( name == "web_ui_v1" )
130 metadata.web_ui_v1 = value.get<bool>();
131 else if( name == "parts_v1" )
132 metadata.parts_v1 = value.get<bool>();
133 else if( name == "direct_downloads_v1" )
134 metadata.direct_downloads_v1 = value.get<bool>();
135 else if( name == "inline_payloads_v1" )
136 metadata.inline_payloads_v1 = value.get<bool>();
137 }
138
139 if( aJson.contains( "parts" ) )
140 {
141 const nlohmann::json& parts = aJson.at( "parts" );
142 metadata.parts_endpoint_template = RemoteProviderJsonString( parts, "endpoint_template" );
143 }
144 }
145 catch( const std::exception& e )
146 {
147 aError = wxString::Format( _( "Unable to parse remote provider metadata: %s" ),
148 wxString::FromUTF8( e.what() ) );
149 return std::nullopt;
150 }
151
152 if( !metadata.web_ui_v1 )
153 {
154 aError = _( "Remote provider metadata must enable web_ui_v1." );
155 return std::nullopt;
156 }
157
158 if( !metadata.direct_downloads_v1 && !metadata.inline_payloads_v1 )
159 {
160 aError = _(
161 "Remote provider metadata must enable direct_downloads_v1, inline_payloads_v1, or both." );
162 return std::nullopt;
163 }
164
166 && metadata.session_bootstrap_url.IsEmpty() )
167 {
168 aError = _( "Remote provider metadata must define session_bootstrap_url for oauth2." );
169 return std::nullopt;
170 }
171
172 if( !ValidateRemoteUrlSecurity( metadata.api_base_url, metadata.allow_insecure_localhost, aError,
173 _( "api_base_url" ) ) )
174 {
175 return std::nullopt;
176 }
177
178 if( !ValidateRemoteUrlSecurity( metadata.panel_url, metadata.allow_insecure_localhost, aError,
179 _( "panel_url" ) ) )
180 {
181 return std::nullopt;
182 }
183
185 _( "session_bootstrap_url" ) ) )
186 {
187 return std::nullopt;
188 }
189
192 {
193 aError = _( "session_bootstrap_url must share the same origin as panel_url." );
194 return std::nullopt;
195 }
196
199 aError, _( "auth.metadata_url" ) ) )
200 {
201 return std::nullopt;
202 }
203
204 return metadata;
205}
const char * name
Collects JSON-schema validation errors so the caller can inspect them after a validation pass.
nlohmann::json Validate(const nlohmann::json &aJson, nlohmann::json_schema::error_handler &aErrorHandler, const nlohmann::json_uri &aInitialUri=nlohmann::json_uri("#")) const
static wxString GetStockDataPath(bool aRespectRunFromBuildDir=true)
Gets the stock (install) data path, which is the base path for things like scripting,...
Definition paths.cpp:233
#define _(s)
wxString NormalizedUrlOrigin(const wxString &aUrl)
Return a normalized scheme://host:port origin string for aUrl.
wxString RemoteProviderJsonString(const nlohmann::json &aObject, const char *aKey)
Extract an optional string value from a JSON object, returning an empty wxString when the key is abse...
bool ValidateRemoteUrlSecurity(const wxString &aUrl, bool aAllowInsecureLocalhost, wxString &aError, const wxString &aLabel)
Validate that aUrl uses HTTPS, or HTTP on a loopback address when aAllowInsecureLocalhost is true.
static std::optional< REMOTE_PROVIDER_METADATA > FromJson(const nlohmann::json &aJson, wxString &aError)
std::vector< wxString > supported_asset_types
REMOTE_PROVIDER_AUTH_METADATA auth
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition wx_filename.h:39