2019-04-01 10:01:56 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed in accordance with the terms specified in
|
|
|
|
* the LICENSE file found in the root directory of this source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <osquery/utils/schemer/json/schemer_json_error.h>
|
|
|
|
|
|
|
|
#include <osquery/utils/expected/expected.h>
|
|
|
|
#include <osquery/utils/json/json.h>
|
|
|
|
#include <osquery/utils/schemer/schemer.h>
|
|
|
|
|
Support of nested type in schemer json formatter (#5547)
Summary:
Pull Request resolved: https://github.com/facebook/osquery/pull/5547
Since this diff an object of a class with defined schema (see type trait
schemer::has_schema) are allowed as memebers of anoter class with schema.
Example. C++ classes:
```
class Simple {
int alpha = 1;
public:
template <typename Archive, typename ValueType>
static void discloseSchema(Archive& a, ValueType& value) {
schemer::record(a, "alpha", value.alpha);
}
};
class Nested {
Frist beta;
int gama = 2;
public:
template <typename Archive, typename ValueType>
static void discloseSchema(Archive& a, ValueType& value) {
schemer::record(a, "beta", value.beta);
schemer::record(a, "gama", value.gama);
}
};
```
Json representation of `Nested`:
```
{
"beta": {
"alpha": 1
},
"gama": 2
}
```
Reviewed By: SAlexandru
Differential Revision: D14683589
fbshipit-source-id: 1f9e2f862d2bf64be166a717e49cf0f470f8ee36
2019-04-01 16:22:44 +00:00
|
|
|
#include <algorithm>
|
2019-04-01 10:01:56 +00:00
|
|
|
#include <string>
|
|
|
|
|
|
|
|
namespace osquery {
|
|
|
|
namespace schemer {
|
|
|
|
namespace impl {
|
|
|
|
|
|
|
|
template <typename WriterType,
|
|
|
|
typename ValueType,
|
|
|
|
typename std::enable_if<std::is_same<ValueType, bool>::value,
|
|
|
|
int>::type = 0>
|
|
|
|
void writeValue(WriterType& writer, ValueType value) {
|
|
|
|
writer.Bool(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <
|
|
|
|
typename WriterType,
|
|
|
|
typename ValueType,
|
|
|
|
typename std::enable_if<std::is_same<ValueType, std::int8_t>::value ||
|
|
|
|
std::is_same<ValueType, std::int16_t>::value ||
|
|
|
|
std::is_same<ValueType, std::int32_t>::value,
|
|
|
|
int>::type = 0>
|
|
|
|
void writeValue(WriterType& writer, ValueType value) {
|
|
|
|
writer.Int(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename WriterType,
|
|
|
|
typename ValueType,
|
|
|
|
typename std::enable_if<std::is_same<ValueType, std::int64_t>::value,
|
|
|
|
int>::type = 0>
|
|
|
|
void writeValue(WriterType& writer, ValueType const& value) {
|
|
|
|
writer.Int64(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <
|
|
|
|
typename WriterType,
|
|
|
|
typename ValueType,
|
|
|
|
typename std::enable_if<std::is_same<ValueType, std::uint8_t>::value ||
|
|
|
|
std::is_same<ValueType, std::uint16_t>::value ||
|
|
|
|
std::is_same<ValueType, std::uint32_t>::value,
|
|
|
|
int>::type = 0>
|
|
|
|
void writeValue(WriterType& writer, ValueType value) {
|
|
|
|
writer.Uint(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename WriterType,
|
|
|
|
typename ValueType,
|
|
|
|
typename std::enable_if<std::is_same<ValueType, std::uint64_t>::value,
|
|
|
|
int>::type = 0>
|
|
|
|
void writeValue(WriterType& writer, ValueType const& value) {
|
|
|
|
writer.Uint64(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename WriterType,
|
|
|
|
typename ValueType,
|
|
|
|
typename std::enable_if<std::is_floating_point<ValueType>::value,
|
|
|
|
int>::type = 0>
|
|
|
|
void writeValue(WriterType& writer, ValueType const& value) {
|
|
|
|
writer.Double(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename WriterType,
|
|
|
|
typename ValueType,
|
|
|
|
typename std::enable_if<std::is_same<ValueType, std::string>::value,
|
|
|
|
int>::type = 0>
|
|
|
|
void writeValue(WriterType& writer, ValueType const& value) {
|
|
|
|
writer.String(value);
|
|
|
|
}
|
|
|
|
|
Support of nested type in schemer json formatter (#5547)
Summary:
Pull Request resolved: https://github.com/facebook/osquery/pull/5547
Since this diff an object of a class with defined schema (see type trait
schemer::has_schema) are allowed as memebers of anoter class with schema.
Example. C++ classes:
```
class Simple {
int alpha = 1;
public:
template <typename Archive, typename ValueType>
static void discloseSchema(Archive& a, ValueType& value) {
schemer::record(a, "alpha", value.alpha);
}
};
class Nested {
Frist beta;
int gama = 2;
public:
template <typename Archive, typename ValueType>
static void discloseSchema(Archive& a, ValueType& value) {
schemer::record(a, "beta", value.beta);
schemer::record(a, "gama", value.gama);
}
};
```
Json representation of `Nested`:
```
{
"beta": {
"alpha": 1
},
"gama": 2
}
```
Reviewed By: SAlexandru
Differential Revision: D14683589
fbshipit-source-id: 1f9e2f862d2bf64be166a717e49cf0f470f8ee36
2019-04-01 16:22:44 +00:00
|
|
|
template <typename WriterType>
|
|
|
|
class JsonWriter;
|
|
|
|
|
|
|
|
template <typename WriterType,
|
|
|
|
typename ValueType,
|
|
|
|
typename std::enable_if<has_schema<ValueType>::value, int>::type = 0>
|
|
|
|
void writeValue(WriterType& writer, ValueType const& value) {
|
|
|
|
auto next_writer = impl::JsonWriter<WriterType>(writer);
|
|
|
|
ValueType::discloseSchema(next_writer, value);
|
|
|
|
}
|
|
|
|
|
2019-04-01 10:01:56 +00:00
|
|
|
template <typename WriterType>
|
|
|
|
class JsonWriter final {
|
|
|
|
public:
|
|
|
|
explicit JsonWriter(WriterType& writer) : writer_(writer) {
|
|
|
|
writer_.StartObject();
|
|
|
|
}
|
|
|
|
|
|
|
|
~JsonWriter() {
|
|
|
|
writer_.EndObject();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename KeyType, typename ValueType>
|
|
|
|
void record(const KeyType& key, ValueType const& value) {
|
|
|
|
writer_.Key(key);
|
|
|
|
writeValue(writer_, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
WriterType& writer_;
|
|
|
|
};
|
|
|
|
|
2019-04-01 16:22:44 +00:00
|
|
|
class JsonReader final {
|
|
|
|
public:
|
|
|
|
explicit JsonReader(rapidjson::Value const& jObject) : jObject_(jObject) {
|
|
|
|
status.ignoreResult();
|
Support of nested type in schemer json formatter (#5547)
Summary:
Pull Request resolved: https://github.com/facebook/osquery/pull/5547
Since this diff an object of a class with defined schema (see type trait
schemer::has_schema) are allowed as memebers of anoter class with schema.
Example. C++ classes:
```
class Simple {
int alpha = 1;
public:
template <typename Archive, typename ValueType>
static void discloseSchema(Archive& a, ValueType& value) {
schemer::record(a, "alpha", value.alpha);
}
};
class Nested {
Frist beta;
int gama = 2;
public:
template <typename Archive, typename ValueType>
static void discloseSchema(Archive& a, ValueType& value) {
schemer::record(a, "beta", value.beta);
schemer::record(a, "gama", value.gama);
}
};
```
Json representation of `Nested`:
```
{
"beta": {
"alpha": 1
},
"gama": 2
}
```
Reviewed By: SAlexandru
Differential Revision: D14683589
fbshipit-source-id: 1f9e2f862d2bf64be166a717e49cf0f470f8ee36
2019-04-01 16:22:44 +00:00
|
|
|
if (!jObject_.IsObject()) {
|
|
|
|
status =
|
|
|
|
createError(JsonError::TypeMismatch)
|
|
|
|
<< "Wrong type of value: " << jValueToStringForErrorMessage(jObject_)
|
|
|
|
<< ", expected object";
|
|
|
|
}
|
2019-04-01 16:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename KeyType, typename ValueType>
|
|
|
|
void record(const KeyType& key, ValueType& value) {
|
|
|
|
if (status.isError()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto const it = jObject_.FindMember(key);
|
|
|
|
if (it == jObject_.MemberEnd()) {
|
|
|
|
status = createError(JsonError::MissedKey)
|
|
|
|
<< "Missed mandatory key " << key;
|
|
|
|
} else {
|
|
|
|
copyValueFromJValue(key, value, it->value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline std::string jValueToStringForErrorMessage(
|
|
|
|
rapidjson::Value const& jObject) {
|
|
|
|
auto buf = rapidjson::StringBuffer{};
|
|
|
|
rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
|
|
|
|
jObject.Accept(writer);
|
|
|
|
// make sure string representation of value is not too long
|
|
|
|
std::size_t const kMaxLength = 22u;
|
|
|
|
if (buf.GetSize() < kMaxLength) {
|
|
|
|
return std::string{buf.GetString(), buf.GetSize()};
|
|
|
|
} else {
|
|
|
|
return std::string{buf.GetString(), kMaxLength - 3} + "...";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename KeyType,
|
|
|
|
typename ValueType,
|
|
|
|
typename std::enable_if<std::is_same<ValueType, std::string>::value,
|
|
|
|
int>::type = 0>
|
|
|
|
void copyValueFromJValue(const KeyType& key,
|
|
|
|
ValueType& value,
|
|
|
|
rapidjson::Value const& jValue) {
|
|
|
|
if (jValue.IsString()) {
|
|
|
|
value.assign(jValue.GetString(), jValue.GetStringLength());
|
|
|
|
} else {
|
|
|
|
status = createError(JsonError::TypeMismatch)
|
|
|
|
<< "Wrong type of value in pair {\"" << key
|
|
|
|
<< "\":" << jValueToStringForErrorMessage(jValue)
|
|
|
|
<< "}, expected string";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename KeyType,
|
|
|
|
typename ValueType,
|
2019-07-25 20:08:40 +00:00
|
|
|
typename std::enable_if<std::is_same<ValueType, double>::value,
|
2019-04-01 16:22:44 +00:00
|
|
|
int>::type = 0>
|
|
|
|
void copyValueFromJValue(const KeyType& key,
|
|
|
|
ValueType& value,
|
|
|
|
rapidjson::Value const& jValue) {
|
|
|
|
if (jValue.IsNumber()) {
|
|
|
|
value = jValue.GetDouble();
|
|
|
|
} else {
|
|
|
|
status = createError(JsonError::TypeMismatch)
|
|
|
|
<< "Wrong type of value in pair {\"" << key
|
|
|
|
<< "\":" << jValueToStringForErrorMessage(jValue)
|
|
|
|
<< "}, expected floating point number";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename KeyType,
|
|
|
|
typename ValueType,
|
|
|
|
typename std::enable_if<std::is_integral<ValueType>::value,
|
|
|
|
int>::type = 0>
|
|
|
|
void copyValueFromJValue(const KeyType& key,
|
|
|
|
ValueType& value,
|
|
|
|
rapidjson::Value const& jValue) {
|
|
|
|
if (jValue.template Is<ValueType>()) {
|
|
|
|
value = jValue.template Get<ValueType>();
|
|
|
|
} else {
|
|
|
|
status = createError(JsonError::TypeMismatch)
|
|
|
|
<< "Wrong type of value in pair {\"" << key
|
|
|
|
<< "\":" << jValueToStringForErrorMessage(jValue)
|
|
|
|
<< "}, expected "
|
|
|
|
<< boost::core::demangle(typeid(ValueType).name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Support of nested type in schemer json formatter (#5547)
Summary:
Pull Request resolved: https://github.com/facebook/osquery/pull/5547
Since this diff an object of a class with defined schema (see type trait
schemer::has_schema) are allowed as memebers of anoter class with schema.
Example. C++ classes:
```
class Simple {
int alpha = 1;
public:
template <typename Archive, typename ValueType>
static void discloseSchema(Archive& a, ValueType& value) {
schemer::record(a, "alpha", value.alpha);
}
};
class Nested {
Frist beta;
int gama = 2;
public:
template <typename Archive, typename ValueType>
static void discloseSchema(Archive& a, ValueType& value) {
schemer::record(a, "beta", value.beta);
schemer::record(a, "gama", value.gama);
}
};
```
Json representation of `Nested`:
```
{
"beta": {
"alpha": 1
},
"gama": 2
}
```
Reviewed By: SAlexandru
Differential Revision: D14683589
fbshipit-source-id: 1f9e2f862d2bf64be166a717e49cf0f470f8ee36
2019-04-01 16:22:44 +00:00
|
|
|
template <
|
|
|
|
typename KeyType,
|
|
|
|
typename ValueType,
|
|
|
|
typename std::enable_if<has_schema<ValueType>::value, int>::type = 0>
|
|
|
|
void copyValueFromJValue(const KeyType& key,
|
|
|
|
ValueType& value,
|
|
|
|
rapidjson::Value const& jValue) {
|
|
|
|
auto next_reader = impl::JsonReader{jValue};
|
|
|
|
if (next_reader.status.isError()) {
|
|
|
|
status = std::move(next_reader.status);
|
|
|
|
} else {
|
|
|
|
ValueType::discloseSchema(next_reader, value);
|
|
|
|
if (next_reader.status.isError()) {
|
|
|
|
status = std::move(next_reader.status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-01 16:22:44 +00:00
|
|
|
public:
|
|
|
|
ExpectedSuccess<JsonError> status = Success{};
|
|
|
|
|
|
|
|
private:
|
|
|
|
rapidjson::Value const& jObject_;
|
|
|
|
};
|
|
|
|
|
2019-04-01 10:01:56 +00:00
|
|
|
} // namespace impl
|
|
|
|
} // namespace schemer
|
|
|
|
} // namespace osquery
|