]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: ldap token glue
authorMatt Benjamin <mbenjamin@redhat.com>
Thu, 18 Feb 2016 16:26:03 +0000 (11:26 -0500)
committerMatt Benjamin <mbenjamin@redhat.com>
Wed, 9 Mar 2016 15:54:35 +0000 (10:54 -0500)
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 <mbenjamin@redhat.com>
ceph.spec.in
debian/radosgw.install
src/.gitignore
src/CMakeLists.txt
src/rgw/Makefile.am
src/rgw/rgw_token.cc [new file with mode: 0644]
src/rgw/rgw_token.h [new file with mode: 0644]
src/test/CMakeLists.txt
src/test/Makefile-client.am
src/test/test_rgw_token.cc [new file with mode: 0644]

index 3a36ab2259e538d42f2b57cdb87383c12e572ab8..3fa41f9367a81c9381fe41480de12491b8a1863a 100644 (file)
@@ -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*
index 25f0cddaa107d45f3ff66abb6f267abf03a8e9af..f70afed150daaf0b6adb896ecc35fc257c9c82b5 100644 (file)
@@ -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
index 22d42aeffbd5c6226a4866bc237d4134254868b0..bece7abbbe7797b038d25e8e1597c623743637d2 100644 (file)
@@ -74,6 +74,7 @@ Makefile
 /rados
 /radosgw
 /radosgw-admin
+/radosgw-token
 /radosgw-object-expirer
 /rbd
 /rbd-mirror
index aaac2ba8cbc041e3794f2ee6b3c6cfccd9ca2660..5e5aaa23392e9204fac4dd8ff564fbb53adc0110 100644 (file)
@@ -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
index 37e92a1e03ce4b0521a35be0a303be45bf807ac3..86ea5e0580d7f62159a8631f8701eb7470204ed4 100644 (file)
@@ -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 (file)
index 0000000..95be3da
--- /dev/null
@@ -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 <errno.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#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<const char*> 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 (file)
index 0000000..df10314
--- /dev/null
@@ -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 <stdint.h>
+#include <boost/algorithm/string.hpp>
+#include <sstream>
+
+#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 << "<<RGWToken"
+       << " type=" << RGWToken::from_type(token.type)
+       << " id=" << token.id
+       << " key=" << token.key
+       << ">>";
+    return os;
+  }
+
+} /* namespace rgw */
+
+#endif /* RGW_TOKEN_H */
index 895437cc880651132841c736bc3dd95974135f73..a1c0ed2a11684f87d075ed19638f3217385a991d 100644 (file)
@@ -2210,6 +2210,22 @@ target_link_libraries(test_librgw_file_aw
   ${CMAKE_DL_LIBS}
   )
 
+add_executable(test_rgw_token
+  test_rgw_token.cc
+  $<TARGET_OBJECTS:heap_profiler_objs>
+  )
+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
index 987e773d04e9326d025d40c0bae809159675618a..14a8323b5da491f27377427438d7de54b2503402 100644 (file)
@@ -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 (file)
index 0000000..d3b0af0
--- /dev/null
@@ -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 <errno.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#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<RGWToken> 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<const char*> args;
+
+  argv_to_vec(argc, const_cast<const char**>(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();
+}