From: Matt Benjamin Date: Thu, 18 Feb 2016 16:26:03 +0000 (-0500) Subject: rgw: ldap token glue X-Git-Tag: v10.1.0~127^2~4 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=2bb6ef9d84beb9dd6b47eacf79989e7c27eeb272;p=ceph.git rgw: ldap token glue Based on feedback from upstream RGW, introduce a new json-format token structure representing credentials for pass-through authentication (e.g., LDAP without Keystone's digest authentication). Signed-off-by: Matt Benjamin --- diff --git a/ceph.spec.in b/ceph.spec.in index 3a36ab2259e5..3fa41f9367a8 100644 --- a/ceph.spec.in +++ b/ceph.spec.in @@ -1108,6 +1108,7 @@ fi %defattr(-,root,root,-) %{_bindir}/radosgw %{_bindir}/radosgw-admin +%{_bindir}/radosgw-token %{_bindir}/radosgw-object-expirer %{_mandir}/man8/radosgw.8* %{_mandir}/man8/radosgw-admin.8* diff --git a/debian/radosgw.install b/debian/radosgw.install index 25f0cddaa107..f70afed150da 100644 --- a/debian/radosgw.install +++ b/debian/radosgw.install @@ -1,6 +1,7 @@ etc/bash_completion.d/radosgw-admin usr/bin/radosgw usr/bin/radosgw-admin +usr/bin/radosgw-token usr/bin/radosgw-object-expirer usr/share/man/man8/radosgw-admin.8 usr/share/man/man8/radosgw.8 diff --git a/src/.gitignore b/src/.gitignore index 22d42aeffbd5..bece7abbbe77 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -74,6 +74,7 @@ Makefile /rados /radosgw /radosgw-admin +/radosgw-token /radosgw-object-expirer /rbd /rbd-mirror diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aaac2ba8cbc0..5e5aaa23392e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1215,6 +1215,9 @@ if(${WITH_RADOSGW}) rgw/rgw_admin.cc rgw/rgw_orphan.cc) + set(radosgw_token_srcs + rgw/rgw_token.cc) + set(radosgw_object_expirer_srcs rgw/rgw_object_expirer.cc) @@ -1264,6 +1267,11 @@ if(${WITH_RADOSGW}) install(TARGETS radosgw-admin DESTINATION bin) + add_executable(radosgw-token ${radosgw_token_srcs}) + target_link_libraries(radosgw-token librados + global ${ALLOC_LIBS}) + install(TARGETS radosgw-token DESTINATION bin) + add_executable(radosgw-object-expirer ${radosgw_object_expirer_srcs}) target_link_libraries(radosgw-object-expirer rgw_a librados cls_rgw_client cls_lock_client cls_refcount_client diff --git a/src/rgw/Makefile.am b/src/rgw/Makefile.am index 37e92a1e03ce..86ea5e0580d7 100644 --- a/src/rgw/Makefile.am +++ b/src/rgw/Makefile.am @@ -152,6 +152,10 @@ radosgw_admin_SOURCES = rgw/rgw_admin.cc rgw/rgw_orphan.cc radosgw_admin_LDADD = $(LIBRGW) $(LIBRGW_DEPS) $(CEPH_GLOBAL) bin_PROGRAMS += radosgw-admin +radosgw_token_SOURCES = rgw/rgw_token.cc +radosgw_token_LDADD = $(LIBRGW) $(LIBRGW_DEPS) $(CEPH_GLOBAL) +bin_PROGRAMS += radosgw-token + radosgw_object_expirer_SOURCES = rgw/rgw_object_expirer.cc radosgw_object_expirer_LDADD = $(LIBRGW) $(LIBRGW_DEPS) $(CEPH_GLOBAL) bin_PROGRAMS += radosgw-object-expirer @@ -179,6 +183,7 @@ noinst_HEADERS += \ rgw/rgw_cr_rest.h \ rgw/rgw_fcgi.h \ rgw/rgw_xml.h \ + rgw/rgw_token.h \ rgw/rgw_basic_types.h \ rgw/rgw_cache.h \ rgw/rgw_common.h \ diff --git a/src/rgw/rgw_token.cc b/src/rgw/rgw_token.cc new file mode 100644 index 000000000000..95be3da8631a --- /dev/null +++ b/src/rgw/rgw_token.cc @@ -0,0 +1,134 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2016 Red Hat, Inc. + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include +#include +#include +#include + +#include "common/config.h" +#include "common/ceph_argparse.h" +#include "common/debug.h" +#include "global/global_init.h" +#include "include/assert.h" +#include "include/str_list.h" + +#include "rgw_token.h" +#include "rgw_b64.h" + +#define dout_subsys ceph_subsys_rgw + +namespace { + + using namespace rgw; + using std::get; + using std::string; + + RGWToken::token_type type{RGWToken::TOKEN_NONE}; + string access_key{""}; + string secret_key{""}; + + Formatter* formatter{nullptr}; + + bool verbose {false}; + bool do_encode {false}; + bool do_decode {false}; + +} + +void usage() +{ + cout << "usage: radosgw-token [options...]" << std::endl; + cout << "\t(maybe exporting RGW_ACCESS_KEY_ID and RGW_SECRET_ACCESS_KEY)" + << std::endl; + cout << "\n"; + generic_client_usage(); +} + +int main(int argc, char **argv) +{ + std::string val; + vector args; + argv_to_vec(argc, (const char **)argv, args); + env_to_vec(args); + + global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0); + common_init_finish(g_ceph_context); + + char *v{nullptr}; + v = getenv("RGW_ACCESS_KEY_ID"); + if (v) { + access_key = v; + } + + v = getenv("RGW_SECRET_ACCESS_KEY"); + if (v) { + secret_key = v; + } + + for (auto arg_iter = args.begin(); arg_iter != args.end();) { + if (ceph_argparse_witharg(args, arg_iter, &val, "--access", + (char*) nullptr)) { + access_key = val; + } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret", + (char*) nullptr)) { + secret_key = val; + } else if (ceph_argparse_witharg(args, arg_iter, &val, "--ttype", + (char*) nullptr)) { + for (const auto& ttype : {"ad", "ldap"}) { + if (boost::iequals(val, ttype)) { + type = RGWToken::to_type(val); + break; + } + } + } else if (ceph_argparse_flag(args, arg_iter, "--encode", + (char*) nullptr)) { + do_encode = true; + } else if (ceph_argparse_flag(args, arg_iter, "--decode", + (char*) nullptr)) { + do_decode = true; + } else if (ceph_argparse_flag(args, arg_iter, "--verbose", + (char*) nullptr)) { + verbose = true; + } else { + ++arg_iter; + } + } + + if ((! do_encode) || + (type == RGWToken::TOKEN_NONE)) { + usage(); + return -EINVAL; + } + + formatter = new JSONFormatter(true /* pretty */); + + RGWToken token(type, access_key, secret_key); + if (do_encode) { + token.encode_json(formatter); + std::ostringstream os; + formatter->flush(os); + string token_str = os.str(); + if (verbose) { + std::cout << "expanded token: " << token_str << std::endl; + if (do_decode) { + RGWToken token2(token_str); + std::cout << "decoded expanded token: " << token2 << std::endl; + } + } + std::cout << to_base64(token_str) << std::endl; + } + + return 0; +} diff --git a/src/rgw/rgw_token.h b/src/rgw/rgw_token.h new file mode 100644 index 000000000000..df10314801cf --- /dev/null +++ b/src/rgw/rgw_token.h @@ -0,0 +1,165 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2016 Red Hat, Inc + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#ifndef RGW_TOKEN_H +#define RGW_TOKEN_H + +#include +#include +#include + +#include "common/ceph_json.h" +#include "common/Formatter.h" +#include "rgw/rgw_b64.h" + +namespace rgw { + + using std::string; + + class RGWToken { + public: + static constexpr auto type_name = "RGW_TOKEN"; + + enum token_type : uint32_t { + TOKEN_NONE, + TOKEN_AD, + TOKEN_KEYSTONE, + TOKEN_LDAP, + }; + + static enum token_type to_type(const string& s) { + if (boost::iequals(s, "ad")) + return TOKEN_AD; + if (boost::iequals(s, "ldap")) + return TOKEN_LDAP; + if (boost::iequals(s, "keystone")) + return TOKEN_KEYSTONE; + return TOKEN_NONE; + } + + static const char* from_type(enum token_type type) { + switch (type) { + case TOKEN_AD: + return "ad"; + break; + case TOKEN_LDAP: + return "ldap"; + break; + case TOKEN_KEYSTONE: + return "keystone"; + break; + default: + return "none"; + }; + return "none"; + } + + token_type type; + string id; + string key; + + virtual uint32_t version() const { return 1; }; + + bool valid() { + return ((type != TOKEN_NONE) && + (! id.empty()) && + (! key.empty())); + } + + RGWToken() + : type(TOKEN_NONE) {}; + + RGWToken(enum token_type _type, const std::string& _id, + const std::string& _key) + : type(_type), id(_id), key(_key) {}; + + RGWToken(const string& json) { + JSONParser p; + p.parse(json.c_str(), json.length()); + JSONDecoder::decode_json(RGWToken::type_name, *this, &p); + } + + void encode(bufferlist& bl) const { + uint32_t ver = version(); + string typestr{from_type(type)}; + ::encode(type_name, bl); + ::encode(ver, bl); + ::encode(typestr, bl); + ::encode(id, bl); + ::encode(key, bl); + } + + void decode(bufferlist::iterator& bl) { + string name; + string typestr; + uint32_t version; + ::decode(name, bl); + ::decode(version, bl); + ::decode(typestr, bl); + type = to_type(typestr.c_str()); + ::decode(id, bl); + ::decode(key, bl); + } + + void dump(Formatter* f) const { + ::encode_json("version", uint32_t(version()), f); + ::encode_json("type", from_type(type), f); + ::encode_json("id", id, f); + ::encode_json("key", key, f); + } + + void encode_json(Formatter* f) { + RGWToken& token = *this; + f->open_object_section(type_name); + ::encode_json(type_name, token, f); + f->close_section(); + } + + void decode_json(JSONObj* obj) { + uint32_t version; + string type_name; + string typestr; + JSONDecoder::decode_json("version", version, obj); + JSONDecoder::decode_json("type", typestr, obj); + type = to_type(typestr.c_str()); + JSONDecoder::decode_json("id", id, obj); + JSONDecoder::decode_json("key", key, obj); + } + + std::string encode_json_base64(Formatter* f) { + encode_json(f); + std::ostringstream os; + f->flush(os); + return std::move(to_base64(std::move(os.str()))); + } + + friend inline ostream& operator<<(ostream& os, const RGWToken& token); + + virtual ~RGWToken() {}; + }; + WRITE_CLASS_ENCODER(RGWToken) + + inline ostream& operator<<(ostream& os, const RGWToken& token) + { + os << "<>"; + return os; + } + +} /* namespace rgw */ + +#endif /* RGW_TOKEN_H */ diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 895437cc8806..a1c0ed2a1168 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -2210,6 +2210,22 @@ target_link_libraries(test_librgw_file_aw ${CMAKE_DL_LIBS} ) +add_executable(test_rgw_token + test_rgw_token.cc + $ + ) +set_target_properties(test_rgw_token PROPERTIES COMPILE_FLAGS + ${UNITTEST_CXX_FLAGS}) +target_link_libraries(test_rgw_token + rgw + os + global + ${UNITTEST_LIBS} + ${EXTRALIBS} + ${ALLOC_LIBS} + ${CMAKE_DL_LIBS} + ) + if(${HAVE_LIBFUSE}) add_executable(test_cfuse_cache_invalidate test_cfuse_cache_invalidate.cc diff --git a/src/test/Makefile-client.am b/src/test/Makefile-client.am index 987e773d04e9..14a8323b5da4 100644 --- a/src/test/Makefile-client.am +++ b/src/test/Makefile-client.am @@ -740,6 +740,12 @@ librgw_file_nfsns_LDADD = $(UNITTEST_LDADD) \ librgw.la librados.la $(PTHREAD_LIBS) $(CEPH_GLOBAL) $(EXTRALIBS) bin_DEBUGPROGRAMS += librgw_file_nfsns +test_rgw_token_SOURCES = test/test_rgw_token.cc +test_rgw_token_CXXFLAGS = $(UNITTEST_CXXFLAGS) +test_rgw_token_LDADD = $(UNITTEST_LDADD) \ + librgw.la $(PTHREAD_LIBS) $(LIBOS) $(CEPH_GLOBAL) $(EXTRALIBS) +bin_DEBUGPROGRAMS += test_rgw_token + endif # WITH_RADOSGW diff --git a/src/test/test_rgw_token.cc b/src/test/test_rgw_token.cc new file mode 100644 index 000000000000..d3b0af0df4d4 --- /dev/null +++ b/src/test/test_rgw_token.cc @@ -0,0 +1,97 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2016 Red Hat, Inc. + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include +#include +#include +#include + +#include "common/config.h" +#include "common/ceph_argparse.h" +#include "common/debug.h" +#include "global/global_init.h" +#include "include/assert.h" +#include "gtest/gtest.h" +#include "rgw/rgw_token.h" +#include "rgw/rgw_b64.h" + +#define dout_subsys ceph_subsys_rgw + +namespace { + + using namespace rgw; + using std::get; + using std::string; + + string access_key{"Smonny"}; + string secret_key{"Turjan of Miir"}; + + std::vector tokens; + + std::string enc_ad{"ewogICAgIlJHV19UT0tFTiI6IHsKICAgICAgICAidmVyc2lvbiI6IDEsCiAgICAgICAgInR5cGUiOiAiYWQiLAogICAgICAgICJpZCI6ICJTbW9ubnkiLAogICAgICAgICJrZXkiOiAiVHVyamFuIG9mIE1paXIiCiAgICB9Cn0K"}; + + std::string enc_ldap{"ewogICAgIlJHV19UT0tFTiI6IHsKICAgICAgICAidmVyc2lvbiI6IDEsCiAgICAgICAgInR5cGUiOiAibGRhcCIsCiAgICAgICAgImlkIjogIlNtb25ueSIsCiAgICAgICAgImtleSI6ICJUdXJqYW4gb2YgTWlpciIKICAgIH0KfQo="}; + + Formatter* formatter{nullptr}; + bool verbose {false}; +} + +TEST(TOKEN, INIT) { + formatter = new JSONFormatter(true /* pretty */); + ASSERT_NE(formatter, nullptr); +} + +TEST(TOKEN, ENCODE) { + // encode the two supported types + RGWToken token_ad(RGWToken::TOKEN_AD, access_key, secret_key); + ASSERT_EQ(token_ad.encode_json_base64(formatter), enc_ad); + tokens.push_back(token_ad); // provies copiable + + RGWToken token_ldap(RGWToken::TOKEN_LDAP, access_key, secret_key); + ASSERT_EQ(token_ldap.encode_json_base64(formatter), enc_ldap); + tokens.push_back(token_ldap); +} + +TEST(TOKEN, DECODE) { + for (const auto& enc_tok : {enc_ad, enc_ldap}) { + RGWToken token{from_base64(enc_tok)}; // decode ctor + ASSERT_EQ(token.id, access_key); + ASSERT_EQ(token.key, secret_key); + } +} + +TEST(TOKEN, SHUTDOWN) { + delete formatter; +} + +int main(int argc, char *argv[]) +{ + string val; + vector args; + + argv_to_vec(argc, const_cast(argv), args); + env_to_vec(args); + + for (auto arg_iter = args.begin(); arg_iter != args.end();) { + if (ceph_argparse_flag(args, arg_iter, "--verbose", + (char*) nullptr)) { + verbose = true; + } else { + ++arg_iter; + } + } + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}