Skip to content
Open
Show file tree
Hide file tree
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
Expand Up @@ -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)
Expand All @@ -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 "";
Expand All @@ -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);
Expand All @@ -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 "";
Expand Down Expand Up @@ -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)));
}
}

Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions DynamicKey/AgoraDynamicKey/cpp/src/Packer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
}
Expand Down
100 changes: 99 additions & 1 deletion DynamicKey/AgoraDynamicKey/cpp/test/AccessToken2_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#include "../src/md5/md5.h"

using namespace agora::tools;

class AccessToken2_test : public testing::Test {
protected:
virtual void SetUp() override {
Expand All @@ -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());

Expand Down Expand Up @@ -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_;
Expand All @@ -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_;
};

Expand All @@ -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
Expand Up @@ -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_);
Expand All @@ -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));
Expand All @@ -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_);
Expand All @@ -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_);
Expand Down
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)
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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_);
Expand All @@ -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));
Expand All @@ -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_);
Expand All @@ -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_);
Expand Down