foundationdb/fdbrpc/TokenSign/TokenSignStdTypes.cpp

120 lines
4.3 KiB
C++

/*
* TokenSignStlTypes.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "fdbrpc/TokenSign.h"
#include "fdbrpc/TokenSignStdTypes.h"
#include "flow/PKey.h"
#include "flow/MkCert.h"
#include <stdexcept>
namespace {
// converts std::optional<STANDARD_TYPE(S)> to Optional<FLOW_TYPE(T)>
template <class T, class S>
void convertAndAssign(Arena& arena, Optional<T>& to, const std::optional<S>& from) {
if (!from.has_value()) {
to.reset();
return;
}
if constexpr (std::is_same_v<S, std::vector<std::string>>) {
static_assert(std::is_same_v<T, VectorRef<StringRef>>,
"Source type std::vector<std::string> must convert to VectorRef<StringRef>");
const auto& value = from.value();
if (value.empty()) {
to = VectorRef<StringRef>();
} else {
// no need to deep copy string because we have the underlying memory for the duration of token signing.
auto buf = new (arena) StringRef[value.size()];
for (auto i = 0u; i < value.size(); i++) {
buf[i] = StringRef(value[i]);
}
to = VectorRef<StringRef>(buf, value.size());
}
} else if constexpr (std::is_same_v<S, std::vector<int64_t>>) {
static_assert(std::is_same_v<T, VectorRef<int64_t>>,
"Source type std::vector<int64_t> must convert to VectorRef<int64_t>");
const auto& value = from.value();
if (value.empty()) {
to = VectorRef<int64_t>();
} else {
auto buf = new (arena) int64_t[value.size()];
for (auto i = 0; i < value.size(); i++)
buf[i] = value[i];
to = VectorRef<int64_t>(buf, value.size());
}
} else if constexpr (std::is_same_v<S, std::string>) {
static_assert(std::is_same_v<T, StringRef>, "Source type std::string must convert to StringRef");
const auto& value = from.value();
// no need to deep copy string because we have the underlying memory for the duration of token signing.
to = StringRef(value);
} else {
static_assert(std::is_same_v<S, T>,
"Source types that aren't std::vector<std::string>, std::vector<int64_t>, or std::string must "
"have the same destination type");
static_assert(std::is_trivially_copy_assignable_v<S>,
"Source types that aren't std::vector<std::string> or std::string must not use heap memory");
to = from.value();
}
}
} // anonymous namespace
namespace authz::jwt::stdtypes {
std::string makeEcP256PrivateKeyPem() {
try {
PrivateKey pk = mkcert::makeEcP256();
auto tmpArena = Arena();
auto pem = pk.writePem(tmpArena);
return pem.toString();
} catch (Error& e) {
throw std::runtime_error(e.name());
}
}
std::string signToken(const TokenSpec& tokenSpec, const std::string& privateKeyPem) {
try {
auto arena = Arena();
auto privateKey = PrivateKey(PemEncoded{}, StringRef(privateKeyPem));
// translate TokenSpec (uses STL types) to TokenRef (uses Flow types)
auto t = authz::jwt::TokenRef{};
t.algorithm = tokenSpec.algorithm;
t.keyId = StringRef(tokenSpec.keyId);
convertAndAssign(arena, t.issuer, tokenSpec.issuer);
convertAndAssign(arena, t.subject, tokenSpec.subject);
convertAndAssign(arena, t.audience, tokenSpec.audience);
convertAndAssign(arena, t.issuedAtUnixTime, tokenSpec.issuedAtUnixTime);
convertAndAssign(arena, t.expiresAtUnixTime, tokenSpec.expiresAtUnixTime);
convertAndAssign(arena, t.notBeforeUnixTime, tokenSpec.notBeforeUnixTime);
convertAndAssign(arena, t.tokenId, tokenSpec.tokenId);
convertAndAssign(arena, t.tenants, tokenSpec.tenants);
return signToken(arena, t, privateKey).toString();
} catch (Error& e) {
if (e.code() == error_code_pkey_decode_error) {
// bad PEM
throw std::invalid_argument(e.name());
} else {
throw std::runtime_error(e.name());
}
}
}
} // namespace authz::jwt::stdtypes