Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 42 additions & 5 deletions DynamicKey/AgoraDynamicKey/cpp/src/AccessToken2.h
Original file line number Diff line number Diff line change
@@ -241,6 +241,14 @@ static const std::map<uint16_t, Service *(*)()> kServiceCreator = {
{ServiceApaas::kServiceType, ServiceCreator<ServiceApaas>::New},
};

enum TokenStatus {
kTokenVerifySuccess = 0,
kTokenVerifyFailed = -1,
kTokenInvalid = -2,
kTokenInvalidInfo = -3,
kTokenThrow = -4,
};

class AccessToken2 {
public:
AccessToken2(const std::string &app_id = "", const std::string &app_certificate = "", uint32_t issue_ts = 0, uint32_t expire = 900)
@@ -257,7 +265,7 @@ class AccessToken2 {

static std::string Version() { return "007"; }

void AddService(std::unique_ptr<Service> service) { services_[service->ServiceType()] = std::move(service); }
void AddService(std::unique_ptr<Service> service) { services_.insert(std::make_pair(service->ServiceType(), std::move(service))); }

std::string Build() {
if (!BuildCheck()) return "";
@@ -276,6 +284,7 @@ class AccessToken2 {

try {
auto buffer = Decompress(base64Decode(token.substr(VERSION_LENGTH)));
raw_token_buffer_ = buffer;
Unpacker unpacker(buffer.data(), buffer.length());
unpacker >> signature_ >> app_id_ >> issue_ts_ >> expire_ >> salt_;
UnpackServices(&unpacker);
@@ -285,6 +294,29 @@ class AccessToken2 {
return true;
}

TokenStatus VerifySignature(const std::string &app_certificate) {
app_cert_ = app_certificate;
if (raw_token_buffer_.empty()) {
perror("invalid token, please unpack first by FromString()");
return kTokenInvalid;
} else if (!BuildCheck()) {
return kTokenInvalidInfo;
}
try {
std::string signature;
Unpacker unpacker(raw_token_buffer_.data(), raw_token_buffer_.length());
unpacker >> signature;
auto signing = Signing();

auto signing_info = unpacker.pop_raw_string_to_end();
auto gen_signature = HmacSign2(signing, signing_info, HMAC_SHA256_LENGTH);
return signature == gen_signature ? kTokenVerifySuccess : kTokenVerifyFailed;
} catch (std::exception &e) {
perror((std::string("VerifySignature error: ") + e.what()).c_str());
return kTokenThrow;
}
}

std::string GenerateSignature(const std::string &app_certificate) {
app_cert_ = app_certificate;
if (!BuildCheck()) return "";
@@ -323,10 +355,14 @@ class AccessToken2 {
for (auto i = 0; i < service_count; ++i) {
uint16_t service_type;
*unpacker >> service_type;

auto service = std::unique_ptr<Service>(kServiceCreator.at(service_type)());
auto service_ptr = kServiceCreator.find(service_type);
if (service_ptr == kServiceCreator.end()) {
perror((std::string("invalid service type ") + std::to_string(service_type)).c_str());
break;
}
auto service = std::unique_ptr<Service>(service_ptr->second());
service->UnpackService(unpacker);
services_[service_type] = std::move(service);
services_.insert(std::make_pair(service_type, std::move(service)));
}
}

@@ -357,8 +393,9 @@ class AccessToken2 {
std::string app_id_;
std::string app_cert_;
std::string signature_;
std::string raw_token_buffer_;

std::map<uint16_t, std::unique_ptr<Service>> services_;
std::multimap<uint16_t, std::unique_ptr<Service>> services_;
};

} // namespace tools
8 changes: 8 additions & 0 deletions DynamicKey/AgoraDynamicKey/cpp/src/Packer.h
Original file line number Diff line number Diff line change
@@ -313,6 +313,14 @@ class Unpacker
return s;
}

std::string pop_raw_string_to_end() {
uint32_t length = length_ - position_;
check_size(length, position_);
std::string s = std::string(buffer_ + position_, length);
position_ += length;
return s;
}

const char* buffer() const {
return buffer_;
}
100 changes: 99 additions & 1 deletion DynamicKey/AgoraDynamicKey/cpp/test/AccessToken2_test.cpp
Original file line number Diff line number Diff line change
@@ -13,7 +13,6 @@
#include "../src/md5/md5.h"

using namespace agora::tools;

class AccessToken2_test : public testing::Test {
protected:
virtual void SetUp() override {
@@ -26,11 +25,34 @@ class AccessToken2_test : public testing::Test {
account_ = "2882341273";
expire_ = 600;
issue_ts_ = 1111111;
expiredTs_ = time(nullptr) + 3600;

room_uuid_ = "123";
role_ = 1;
}

std::unique_ptr<Service> BuildRtcService(std::string channelName, uint32_t uid, uint32_t expiredTs) {
std::unique_ptr<Service> rtc(new ServiceRtc(channelName, uid));
rtc->AddPrivilege(ServiceRtc::kPrivilegeJoinChannel, expiredTs);
rtc->AddPrivilege(ServiceRtc::kPrivilegePublishAudioStream, expiredTs);
rtc->AddPrivilege(ServiceRtc::kPrivilegePublishVideoStream, expiredTs);
rtc->AddPrivilege(ServiceRtc::kPrivilegePublishDataStream, expiredTs);
return rtc;
}

std::unique_ptr<Service> BuildRtmService(std::string uidStr, uint32_t expiredTs) {
std::unique_ptr<Service> rtm(new ServiceRtm(uidStr));
rtm->AddPrivilege(ServiceRtm::kPrivilegeLogin, expiredTs);
return rtm;
}

std::unique_ptr<Service> BuildRtmStreamServiceAsRtc(std::string channelName, uint32_t uid, uint32_t expiredTs) {
std::unique_ptr<Service> rtc(new ServiceRtc(channelName, uid));
rtc->AddPrivilege(ServiceRtc::kPrivilegeJoinChannel, expiredTs);
rtc->AddPrivilege(ServiceRtc::kPrivilegePublishDataStream, expiredTs);
return rtc;
}

void VerifyService(Service *l, Service *r) {
EXPECT_EQ(l->privileges_.size(), r->privileges_.size());

@@ -325,6 +347,77 @@ class AccessToken2_test : public testing::Test {

VerifyAccessToken2(expected, &key);
}
void TestSameServiceMulti() {
auto rtc_expire = expiredTs_;
auto rtm_expire = expiredTs_ + 100;
auto rtm_stream_expire = expiredTs_ + 200;

AccessToken2 token(app_id_, app_certificate_, 0, rtc_expire);

token.AddService(std::move(BuildRtcService(channel_name_, uid_, rtc_expire)));
token.AddService(std::move(BuildRtmService(account_, rtm_expire)));
token.AddService(std::move(BuildRtmStreamServiceAsRtc(channel_name_, uid_, rtm_stream_expire)));
std::string token_str = token.Build();
AccessToken2 token_parsed;
ASSERT_EQ(token_parsed.VerifySignature(app_certificate_), kTokenInvalid);
ASSERT_TRUE(token_parsed.FromString(token_str));
ASSERT_EQ(token_parsed.VerifySignature(app_certificate_+"123"), kTokenInvalidInfo);
std::string err_cert = app_certificate_;
err_cert[0] = '1';
ASSERT_EQ(token_parsed.VerifySignature(err_cert), kTokenVerifyFailed);
ASSERT_EQ(token_parsed.VerifySignature(app_certificate_), kTokenVerifySuccess);

EXPECT_EQ(app_id_, token.app_id_);
EXPECT_EQ(rtc_expire, token.expire_);

ASSERT_EQ(3, token.services_.size());
ASSERT_EQ(token.services_.count(ServiceRtc::kServiceType), 2);
ASSERT_EQ(token.services_.count(ServiceRtm::kServiceType), 1);


uint32_t cnt = 0;
for (auto srv = token_parsed.services_.begin(); srv != token_parsed.services_.end(); srv++) {
if (srv->first == ServiceRtc::kServiceType) {
if (cnt == 0 ) {
ServiceRtc *rtc = dynamic_cast<ServiceRtc *>(srv->second.get());
EXPECT_EQ(rtc->channel_name_, channel_name_);
EXPECT_EQ(rtc->account_, account_);
EXPECT_EQ(rtc->privileges_[ServiceRtc::kPrivilegeJoinChannel], rtc_expire);
EXPECT_EQ(rtc->privileges_[ServiceRtc::kPrivilegePublishAudioStream], rtc_expire);
EXPECT_EQ(rtc->privileges_[ServiceRtc::kPrivilegePublishVideoStream], rtc_expire);
EXPECT_EQ(rtc->privileges_[ServiceRtc::kPrivilegePublishDataStream], rtc_expire);
} else if (cnt == 1) {
ServiceRtc *rtm_stream = dynamic_cast<ServiceRtc *>(srv->second.get());
EXPECT_EQ(rtm_stream->channel_name_, channel_name_);
EXPECT_EQ(rtm_stream->account_, account_);
EXPECT_EQ(rtm_stream->privileges_[ServiceRtc::kPrivilegeJoinChannel], rtm_stream_expire);
EXPECT_EQ(rtm_stream->privileges_[ServiceRtc::kPrivilegePublishDataStream], rtm_stream_expire);
}
cnt++;
} else if (srv->first == ServiceRtm::kServiceType) {
ServiceRtm * rtm = dynamic_cast<ServiceRtm *>(srv->second.get());
EXPECT_EQ(rtm->user_id_, account_);
EXPECT_EQ(rtm->privileges_[ServiceRtm::kPrivilegeLogin], rtm_expire);
} else {
EXPECT_TRUE(false);
}
}
EXPECT_EQ(token_parsed.GenerateSignature(app_certificate_), token_parsed.signature_);
}

void TestOldTokenParse() {
std::string token_str = "007eJxTYLjhFiNy2/+8zqRJj20tt73SKA2e3/"
"joPVv4761qZnrOyqYKDJbmBs6OxqYpqWYGySYmZiamSUmJqRaJRoamBmaGScbG7l8EGCKY"
"GBgYGRgYWIAkCIP4TGCSGUyygEkFBvMUcyNjM9PUJEsLYxMLU2NL81TjVOM0yxQTM4OklJ"
"RELgYjCwsjYxNDI3NjJqA5EJM4GUpSi0viS4tTi1jggqxwFrImAAIiLHc=";
AccessToken2 token_parsed;
ASSERT_TRUE(token_parsed.FromString(token_str));
ASSERT_EQ(token_parsed.VerifySignature(app_certificate_), kTokenVerifySuccess);
EXPECT_EQ(token_parsed.app_id_, app_id_);
EXPECT_EQ(token_parsed.expire_, expire_);
EXPECT_EQ(token_parsed.GenerateSignature(app_certificate_), token_parsed.signature_);
VerifyAccessToken2(token_str, &token_parsed);
}

private:
std::string app_id_;
@@ -337,6 +430,7 @@ class AccessToken2_test : public testing::Test {
uint32_t uid_;
uint32_t expire_;
uint32_t issue_ts_;
uint32_t expiredTs_;
int16_t role_;
};

@@ -359,3 +453,7 @@ TEST_F(AccessToken2_test, testAccessToken2ApaasUser) { TestAccessToken2ApaasUser
TEST_F(AccessToken2_test, testAccessToken2ApaasApp) { TestAccessToken2ApaasApp(); }

TEST_F(AccessToken2_test, testAccessToken2WithMultiService) { TestAccessToken2WithMultiService(); }

TEST_F(AccessToken2_test, testSameServiceMulti) { TestSameServiceMulti(); }

TEST_F(AccessToken2_test, testOldTokenParse) { TestOldTokenParse(); }
10 changes: 5 additions & 5 deletions DynamicKey/AgoraDynamicKey/cpp/test/ApaasTokenBuilder_test.cpp
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ class ApaasTokenBuilder_test : public testing::Test {
ASSERT_TRUE(token.services_.count(ServiceRtm::kServiceType));
ASSERT_TRUE(token.services_.count(ServiceChat::kServiceType));

const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_[ServiceApaas::kServiceType]);
const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_.find(ServiceApaas::kServiceType)->second);

EXPECT_EQ(room_uuid_, apaas_service.room_uuid_);
EXPECT_EQ(user_id_, apaas_service.user_uuid_);
@@ -49,13 +49,13 @@ class ApaasTokenBuilder_test : public testing::Test {
ASSERT_TRUE(apaas_service.privileges_.count(ServiceApaas::kPrivilegeRoomUser));
EXPECT_EQ(expire_, apaas_service.privileges_.at(ServiceApaas::kPrivilegeRoomUser));

const auto &rtm_service = dynamic_cast<const ServiceRtm &>(*token.services_[ServiceRtm::kServiceType]);
const auto &rtm_service = dynamic_cast<const ServiceRtm &>(*token.services_.find(ServiceRtm::kServiceType)->second);
EXPECT_EQ(user_id_, rtm_service.user_id_);
ASSERT_EQ(1, rtm_service.privileges_.size());
ASSERT_TRUE(rtm_service.privileges_.count(ServiceRtm::kPrivilegeLogin));
EXPECT_EQ(expire_, rtm_service.privileges_.at(ServiceRtm::kPrivilegeLogin));

const auto &chat_service = dynamic_cast<const ServiceChat &>(*token.services_[ServiceChat::kServiceType]);
const auto &chat_service = dynamic_cast<const ServiceChat &>(*token.services_.find(ServiceChat::kServiceType)->second);
EXPECT_EQ(chat_user_id_, chat_service.user_id_);
ASSERT_EQ(1, chat_service.privileges_.size());
ASSERT_TRUE(chat_service.privileges_.count(ServiceChat::kPrivilegeUser));
@@ -73,7 +73,7 @@ class ApaasTokenBuilder_test : public testing::Test {
ASSERT_EQ(1, token.services_.size());
ASSERT_TRUE(token.services_.count(ServiceApaas::kServiceType));

const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_[ServiceApaas::kServiceType]);
const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_.find(ServiceApaas::kServiceType)->second);

EXPECT_EQ("", apaas_service.room_uuid_);
EXPECT_EQ(user_id_, apaas_service.user_uuid_);
@@ -94,7 +94,7 @@ class ApaasTokenBuilder_test : public testing::Test {
ASSERT_EQ(1, token.services_.size());
ASSERT_TRUE(token.services_.count(ServiceApaas::kServiceType));

const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_[ServiceApaas::kServiceType]);
const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_.find(ServiceApaas::kServiceType)->second);

EXPECT_EQ("", apaas_service.room_uuid_);
EXPECT_EQ("", apaas_service.user_uuid_);
4 changes: 2 additions & 2 deletions DynamicKey/AgoraDynamicKey/cpp/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.13)
cmake_minimum_required(VERSION 3.10)
project(test)

set(CMAKE_CXX_STANDARD 11)
@@ -9,7 +9,7 @@ include_directories(..)
include_directories(../../)

link_directories(/usr/lib)
link_libraries(-lgtest -lz -lcrypto)
link_libraries(-lgtest -lz -lcrypto -lpthread)

add_executable(test.exe
AccessToken_test.cpp
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ class EducationTokenBuilder2_test : public testing::Test {
ASSERT_TRUE(token.services_.count(ServiceRtm::kServiceType));
ASSERT_TRUE(token.services_.count(ServiceChat::kServiceType));

const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_[ServiceApaas::kServiceType]);
const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_.find(ServiceApaas::kServiceType)->second);

EXPECT_EQ(room_uuid_, apaas_service.room_uuid_);
EXPECT_EQ(user_id_, apaas_service.user_uuid_);
@@ -49,13 +49,13 @@ class EducationTokenBuilder2_test : public testing::Test {
ASSERT_TRUE(apaas_service.privileges_.count(ServiceApaas::kPrivilegeRoomUser));
EXPECT_EQ(expire_, apaas_service.privileges_.at(ServiceApaas::kPrivilegeRoomUser));

const auto &rtm_service = dynamic_cast<const ServiceRtm &>(*token.services_[ServiceRtm::kServiceType]);
const auto &rtm_service = dynamic_cast<const ServiceRtm &>(*token.services_.find(ServiceRtm::kServiceType)->second);
EXPECT_EQ(user_id_, rtm_service.user_id_);
ASSERT_EQ(1, rtm_service.privileges_.size());
ASSERT_TRUE(rtm_service.privileges_.count(ServiceRtm::kPrivilegeLogin));
EXPECT_EQ(expire_, rtm_service.privileges_.at(ServiceRtm::kPrivilegeLogin));

const auto &chat_service = dynamic_cast<const ServiceChat &>(*token.services_[ServiceChat::kServiceType]);
const auto &chat_service = dynamic_cast<const ServiceChat &>(*token.services_.find(ServiceChat::kServiceType)->second);
EXPECT_EQ(chat_user_id_, chat_service.user_id_);
ASSERT_EQ(1, chat_service.privileges_.size());
ASSERT_TRUE(chat_service.privileges_.count(ServiceChat::kPrivilegeUser));
@@ -73,7 +73,7 @@ class EducationTokenBuilder2_test : public testing::Test {
ASSERT_EQ(1, token.services_.size());
ASSERT_TRUE(token.services_.count(ServiceApaas::kServiceType));

const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_[ServiceApaas::kServiceType]);
const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_.find(ServiceApaas::kServiceType)->second);

EXPECT_EQ("", apaas_service.room_uuid_);
EXPECT_EQ(user_id_, apaas_service.user_uuid_);
@@ -94,7 +94,7 @@ class EducationTokenBuilder2_test : public testing::Test {
ASSERT_EQ(1, token.services_.size());
ASSERT_TRUE(token.services_.count(ServiceApaas::kServiceType));

const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_[ServiceApaas::kServiceType]);
const auto &apaas_service = dynamic_cast<const ServiceApaas &>(*token.services_.find(ServiceApaas::kServiceType)->second);

EXPECT_EQ("", apaas_service.room_uuid_);
EXPECT_EQ("", apaas_service.user_uuid_);