Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add unit tests for user switching.

  • Loading branch information...
commit c072d96ee56e534eee4ed769f467b1e7ee582586 1 parent d98ac2c
@FooBarWidget FooBarWidget authored
View
1  .gitignore
@@ -43,6 +43,7 @@ doc/*.html
test/test.log
test/apache2.log
test/config.yml
+test/config.json
test/coverage
test/cxx/CxxTestMain
test/oxt/oxt_test_main
View
7 lib/phusion_passenger/common_library.rb
@@ -419,6 +419,13 @@ def aggregate_sources?
Utils/fib.h
Utils/fibpriv.h
)
+ define_component 'Utils/jsoncpp.o',
+ :source => 'Utils/jsoncpp.cpp',
+ :category => :other,
+ :deps => %w(
+ Utils/json.h
+ Utils/json-forwards.h
+ )
#'BCrypt.o' => %w(
# BCrypt.cpp
View
42 test/config.json.example
@@ -0,0 +1,42 @@
+{
+ //// This file contains system-specific configuration options that the test suite needs.
+ //// Please customize it for your system.
+
+ // These are the usernames and group names of normal, non-administrator
+ // users and groups. Preferably, these are user and group accounts that
+ // are normally not used.
+ //
+ // These users and groups MUST be able to access this 'test' directory,
+ // otherwise the tests will fail.
+
+ //// Good values for OS X:
+ "normal_user_1": "_www",
+ "normal_user_2": "daemon",
+ // Must not be "nobody".
+ "default_user": "_sandbox",
+ // Must not be normal_user_1's primary group.
+ "normal_group_1": "daemon",
+ // Must not be normal_user_2's primary group.
+ "normal_group_2": "_sandbox",
+ // Must not be default_user's primary group. Must not be "nobody".
+ "default_group": "_www",
+
+ ///// Good values for Linux and FreeBSD. Same restrictions apply.
+ //"normal_user_1": "games",
+ //"normal_user_2": "daemon",
+ //"default_user": "man",
+ //"normal_group_1": "daemon",
+ //"normal_group_2": "man",
+ //"default_group": "games",
+
+ // A nonexistant username, group name, user ID and group ID.
+ "nonexistant_user": "xxxxxxxxxxxxxxxxxxx",
+ "nonexistant_group": "xxxxxxxxxxxxxxxxxxx",
+ "nonexistant_uid": 9999,
+ "nonexistant_gid": 9999,
+
+ // If you want to run the Nginx integration tests, then set the following
+ // config option to the full path of the Nginx binary. This Nginx binary *must*
+ // be compiled with Phusion Passenger support!
+ // nginx: /usr/local/sbin/nginx
+}
View
11 test/cxx/ApplicationPool2/DirectSpawnerTest.cpp
@@ -1,5 +1,6 @@
#include <TestSupport.h>
#include <ApplicationPool2/Spawner.h>
+#include <Utils/json.h>
using namespace Passenger;
using namespace Passenger::ApplicationPool2;
@@ -28,17 +29,17 @@ namespace tut {
}
};
- DEFINE_TEST_GROUP(ApplicationPool2_DirectSpawnerTest);
+ DEFINE_TEST_GROUP_WITH_LIMIT(ApplicationPool2_DirectSpawnerTest, 80);
#include "SpawnerTestCases.cpp"
- TEST_METHOD(30) {
+ TEST_METHOD(70) {
// If the application didn't start within the timeout
// then whatever was written to stderr is used as the
// SpawnException error page.
Options options = createOptions();
options.appRoot = "stub";
- options.startCommand = "bash\1" "-c\1" "echo hello world >&2; sleep 60";
+ options.startCommand = "perl\1" "-e\1" "print STDERR \"hello world\\n\"; sleep(60)";
options.startupFile = ".";
options.startTimeout = 300;
@@ -56,13 +57,13 @@ namespace tut {
}
}
- TEST_METHOD(31) {
+ TEST_METHOD(71) {
// If the application crashed during startup without returning
// a proper error response, then its stderr output is used
// as error response instead.
Options options = createOptions();
options.appRoot = "stub";
- options.startCommand = "bash\1" "-c\1" "echo hello world >&2";
+ options.startCommand = "perl\1" "-e\1" "print STDERR \"hello world\\n\"";
options.startupFile = ".";
DirectSpawner spawner(bg.safe, *resourceLocator, generation);
View
13 test/cxx/ApplicationPool2/SmartSpawnerTest.cpp
@@ -1,6 +1,7 @@
#include <TestSupport.h>
#include <ApplicationPool2/Spawner.h>
#include <Logging.h>
+#include <Utils/json.h>
#include <unistd.h>
#include <climits>
#include <signal.h>
@@ -50,11 +51,11 @@ namespace tut {
}
};
- DEFINE_TEST_GROUP(ApplicationPool2_SmartSpawnerTest);
+ DEFINE_TEST_GROUP_WITH_LIMIT(ApplicationPool2_SmartSpawnerTest, 80);
#include "SpawnerTestCases.cpp"
- TEST_METHOD(30) {
+ TEST_METHOD(70) {
// If the preloader has crashed then SmartSpawner will
// restart it and try again.
Options options = createOptions();
@@ -73,7 +74,7 @@ namespace tut {
spawner->spawn(options);
}
- TEST_METHOD(31) {
+ TEST_METHOD(71) {
// If the preloader still crashes after the restart then
// SmartSpawner will throw an exception.
Options options = createOptions();
@@ -90,7 +91,7 @@ namespace tut {
}
}
- TEST_METHOD(32) {
+ TEST_METHOD(72) {
// If the preloader didn't start within the timeout
// then it's killed and an exception is thrown, with
// whatever stderr output as error page.
@@ -123,7 +124,7 @@ namespace tut {
}
}
- TEST_METHOD(33) {
+ TEST_METHOD(73) {
// If the preloader crashed during startup without returning
// a proper error response, then its stderr output is used
// as error response instead.
@@ -155,7 +156,7 @@ namespace tut {
}
}
- TEST_METHOD(34) {
+ TEST_METHOD(74) {
// If the preloader encountered an error, then the resulting SpawnException
// takes note of the process's environment variables.
Options options = createOptions();
View
502 test/cxx/ApplicationPool2/SpawnerTestCases.cpp
@@ -1,17 +1,93 @@
// Included in DirectSpawnerTest.cpp and SmartSpawnerTest.cpp.
+ #define SETUP_USER_SWITCHING_TEST() \
+ if (geteuid() != 0) { \
+ return; \
+ } \
+ TempDirCopy copy("stub/wsgi", "tmp.wsgi"); \
+ addUserSwitchingCode(); \
+ \
+ DeleteFileEventually info1("/tmp/info.txt"); \
+ DeleteFileEventually info2("/tmp/info2.txt"); \
+ \
+ SpawnerPtr spawner; \
+ Options options; \
+ options = createOptions(); \
+ options.appRoot = "tmp.wsgi"; \
+ options.appType = "wsgi"; \
+ options.defaultUser = testConfig["default_user"].asCString(); \
+ options.defaultGroup = testConfig["default_group"].asCString(); \
+ spawner = createSpawner(options)
+
+ #define RUN_USER_SWITCHING_TEST() \
+ spawner->spawn(options); \
+ BufferedIO io(FileDescriptor(open("/tmp/info.txt", O_RDONLY))); \
+ uid_t uid = (uid_t) atol(io.readLine().c_str()); \
+ gid_t gid = (gid_t) atol(io.readLine().c_str()); \
+ string groups = strip(io.readLine()); \
+ /* Avoid compiler warning. */ \
+ (void) uid; (void) gid; (void) groups
+
typedef shared_ptr<Spawner> SpawnerPtr;
+ static void addUserSwitchingCode() {
+ FILE *f = fopen("tmp.wsgi/passenger_wsgi.py", "a");
+ fputs(
+ "\n"
+ "import os\n"
+ "f = open('/tmp/info.txt', 'w')\n"
+ "f.write(str(os.getuid()) + '\\n')\n"
+ "f.write(str(os.getgid()) + '\\n')\n"
+ "f.write(os.popen('groups').read() + '\\n')\n"
+ "f.close()\n",
+ f);
+ fclose(f);
+
+ rename("tmp.wsgi/passenger_wsgi.py", "tmp.wsgi/passenger_wsgi.py.real");
+ symlink("passenger_wsgi.py.real", "tmp.wsgi/passenger_wsgi.py");
+ }
+
static void checkin(ProcessPtr process, Connection *conn) {
process->sockets->front().checkinConnection(*conn);
}
+
+ static string userNameForUid(uid_t uid) {
+ return getpwuid(uid)->pw_name;
+ }
+
+ static string groupNameForGid(gid_t gid) {
+ return getgrgid(gid)->gr_name;
+ }
+
+ static uid_t uidFor(const string &userName) {
+ return getpwnam(userName.c_str())->pw_uid;
+ }
+
+ static gid_t gidFor(const string &groupName) {
+ return getgrnam(groupName.c_str())->gr_gid;
+ }
+
+ static string primaryGroupFor(const string &userName) {
+ gid_t gid = getpwnam(userName.c_str())->pw_gid;
+ return getgrgid(gid)->gr_name;
+ }
+
+ static string strip(const string &str) {
+ if (!str.empty() && str[str.size() - 1] == '\n') {
+ string result = str;
+ result.erase(str.size() - 1, 1);
+ return result;
+ } else {
+ return str;
+ }
+ }
TEST_METHOD(1) {
// Basic spawning test.
Options options = createOptions();
options.appRoot = "stub/rack";
options.startCommand = "ruby\1" "start.rb";
- options.startupFile = "stub/rack/start.rb";
+ options.startupFile = "start.rb";
SpawnerPtr spawner = createSpawner(options);
ProcessPtr process = spawner->spawn(options);
ensure_equals(process->sockets->size(), 1u);
@@ -62,7 +138,7 @@
Options options = createOptions();
options.appRoot = "stub";
options.startCommand = "perl\1" "start_error.pl";
- options.startupFile = "stub/start_error.pl";
+ options.startupFile = "start_error.pl";
SpawnerPtr spawner = createSpawner(options);
try {
spawner->spawn(options);
@@ -82,7 +158,7 @@
Options options = createOptions();
options.appRoot = "stub";
options.startCommand = "perl\1" "start_error.pl\1" "freeze";
- options.startupFile = "stub/start_error.pl";
+ options.startupFile = "start_error.pl";
options.startTimeout = 300;
SpawnerPtr spawner = createSpawner(options);
try {
@@ -99,7 +175,7 @@
Options options = createOptions();
options.appRoot = "stub/rack";
options.startCommand = "ruby\1" "start.rb";
- options.startupFile = "stub/rack/start.rb";
+ options.startupFile = "start.rb";
SpawnerPtr spawner = createSpawner(options);
ProcessPtr process = spawner->spawn(options);
ensure_equals(process->sockets->size(), 1u);
@@ -115,7 +191,7 @@
Options options = createOptions();
options.appRoot = "stub/rack";
options.startCommand = "ruby\1" "start.rb";
- options.startupFile = "stub/rack/start.rb";
+ options.startupFile = "start.rb";
options.environmentVariables.push_back(make_pair("PASSENGER_FOO", "foo"));
options.environmentVariables.push_back(make_pair("PASSENGER_BAR", "bar"));
SpawnerPtr spawner = createSpawner(options);
@@ -198,5 +274,419 @@
}
}
- // User switching works.
// It raises an exception if getStartupCommand() is empty.
+
+ /******* User switching tests *******/
+
+ // If 'user' is set
+ // and 'user' is 'root'
+ TEST_METHOD(20) {
+ // It changes the user to the value of 'defaultUser'.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = "root";
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(userNameForUid(uid), testConfig["default_user"]);
+ }
+
+ TEST_METHOD(21) {
+ // If 'group' is given, it changes group to the given group name.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = "root";
+ options.group = testConfig["normal_group_1"].asCString();
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_1"]);
+ }
+
+ TEST_METHOD(22) {
+ // If 'group' is set to the root group, it changes group to defaultGroup.
+ SETUP_USER_SWITCHING_TEST();
+ string rootGroup = groupNameForGid(0);
+ options.user = "root";
+ options.group = rootGroup.c_str();
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["default_group"]);
+ }
+
+ // and 'group' is set to '!STARTUP_FILE!'"
+ TEST_METHOD(23) {
+ // It changes the group to the startup file's group.
+ SETUP_USER_SWITCHING_TEST();
+ string rootGroup = groupNameForGid(0);
+ options.user = "root";
+ options.group = "!STARTUP_FILE!";
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_1"].asString()));
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_1"]);
+ }
+
+ TEST_METHOD(24) {
+ // If the startup file is a symlink, then it uses the symlink's group, not the target's group
+ SETUP_USER_SWITCHING_TEST();
+ string rootGroup = groupNameForGid(0);
+ options.user = "root";
+ options.group = "!STARTUP_FILE!";
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_2"].asString()));
+ chown("tmp.wsgi/passenger_wsgi.py.real",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_1"].asString()));
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_2"]);
+ }
+
+ TEST_METHOD(25) {
+ // If 'group' is not given, it changes the group to defaultUser's primary group.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = "root";
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid),
+ primaryGroupFor(testConfig["default_user"].asString()));
+ }
+
+ // and 'user' is not 'root'
+ TEST_METHOD(29) {
+ // It changes the user to the given username.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["normal_user_1"].asCString();
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(userNameForUid(uid), testConfig["normal_user_1"]);
+ }
+
+ TEST_METHOD(30) {
+ // If 'group' is given, it changes group to the given group name.
+ // It changes the user to the given username.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["normal_user_1"].asCString();
+ options.group = testConfig["normal_group_1"].asCString();
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_1"].asString());
+ }
+
+ TEST_METHOD(31) {
+ // If 'group' is set to the root group, it changes group to defaultGroup.
+ // It changes the user to the given username.
+ SETUP_USER_SWITCHING_TEST();
+ string rootGroup = groupNameForGid(0);
+ options.user = testConfig["normal_user_1"].asCString();
+ options.group = rootGroup.c_str();
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["default_group"].asString());
+ }
+
+ // and 'group' is set to '!STARTUP_FILE!'
+ TEST_METHOD(32) {
+ // It changes the group to the startup file's group.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["normal_user_1"].asCString();
+ options.group = "!STARTUP_FILE!";
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_1"].asString()));
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid),
+ testConfig["normal_group_1"].asString());
+ }
+
+ TEST_METHOD(33) {
+ // If the startup file is a symlink, then it uses the
+ // symlink's group, not the target's group.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["normal_user_1"].asCString();
+ options.group = "!STARTUP_FILE!";
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_2"].asString()));
+ chown("tmp.wsgi/passenger_wsgi.py.real",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_1"].asString()));
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid),
+ testConfig["normal_group_2"].asString());
+ }
+
+ TEST_METHOD(34) {
+ // If 'group' is not given, it changes the group to the user's primary group.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["normal_user_1"].asCString();
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid),
+ primaryGroupFor(testConfig["normal_user_1"].asString()));
+ }
+
+ // and the given username does not exist
+ TEST_METHOD(38) {
+ // It changes the user to the value of defaultUser.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["nonexistant_user"].asCString();
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(userNameForUid(uid), testConfig["default_user"].asString());
+ }
+
+ TEST_METHOD(39) {
+ // If 'group' is given, it changes group to the given group name.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["nonexistant_user"].asCString();
+ options.group = testConfig["normal_group_1"].asCString();
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_1"].asString());
+ }
+
+ TEST_METHOD(40) {
+ // If 'group' is set to the root group, it changes group to defaultGroup.
+ SETUP_USER_SWITCHING_TEST();
+ string rootGroup = groupNameForGid(0);
+ options.user = testConfig["nonexistant_user"].asCString();
+ options.group = rootGroup;
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["default_group"].asString());
+ }
+
+ // and 'group' is set to '!STARTUP_FILE!'
+ TEST_METHOD(41) {
+ // It changes the group to the startup file's group.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["nonexistant_user"].asCString();
+ options.group = "!STARTUP_FILE!";
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_1"].asString()));
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_1"].asString());
+ }
+
+ TEST_METHOD(42) {
+ // If the startup file is a symlink, then it uses the
+ // symlink's group, not the target's group.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["nonexistant_user"].asCString();
+ options.group = "!STARTUP_FILE!";
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_2"].asString()));
+ chown("tmp.wsgi/passenger_wsgi.py.real",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_1"].asString()));
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_2"].asString());
+ }
+
+ TEST_METHOD(43) {
+ // If 'group' is not given, it changes the group to defaultUser's primary group.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["nonexistant_user"].asCString();
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid),
+ primaryGroupFor(testConfig["default_user"].asString()));
+ }
+
+ // If 'user' is not set
+ // and the startup file's owner exists
+ TEST_METHOD(47) {
+ // It changes the user to the owner of the startup file.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ uidFor(testConfig["normal_user_1"].asString()),
+ (gid_t) -1);
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(userNameForUid(uid), testConfig["normal_user_1"].asString());
+ }
+
+ TEST_METHOD(48) {
+ // If the startup file is a symlink, then it uses the symlink's owner, not the target's owner.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ uidFor(testConfig["normal_user_2"].asString()),
+ (gid_t) -1);
+ chown("tmp.wsgi/passenger_wsgi.py.real",
+ uidFor(testConfig["normal_user_1"].asString()),
+ (gid_t) -1);
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(userNameForUid(uid), testConfig["normal_user_2"].asString());
+ }
+
+ TEST_METHOD(49) {
+ // If 'group' is given, it changes group to the given group name.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ uidFor(testConfig["normal_user_1"].asString()),
+ (gid_t) -1);
+ options.group = testConfig["normal_group_1"].asCString();
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_1"].asString());
+ }
+
+ TEST_METHOD(50) {
+ // If 'group' is set to the root group, it changes group to defaultGroup.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ uidFor(testConfig["normal_user_1"].asString()),
+ (gid_t) -1);
+ string rootGroup = groupNameForGid(0);
+ options.group = rootGroup;
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["default_group"].asString());
+ }
+
+ // and 'group' is set to '!STARTUP_FILE!'
+ TEST_METHOD(51) {
+ // It changes the group to the startup file's group.
+ SETUP_USER_SWITCHING_TEST();
+ options.group = "!STARTUP_FILE!";
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_1"].asString()));
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_1"].asString());
+ }
+
+ TEST_METHOD(52) {
+ // If the startup file is a symlink, then it uses the symlink's
+ // group, not the target's group.
+ SETUP_USER_SWITCHING_TEST();
+ options.group = "!STARTUP_FILE!";
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_2"].asString()));
+ chown("tmp.wsgi/passenger_wsgi.py.real",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_1"].asString()));
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_2"].asString());
+ }
+
+ TEST_METHOD(53) {
+ // If 'group' is not given, it changes the group to the startup file's owner's primary group.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ uidFor(testConfig["normal_user_1"].asString()),
+ (gid_t) -1);
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid),
+ primaryGroupFor(testConfig["normal_user_1"].asString()));
+ }
+
+ // and the startup file's owner doesn't exist
+ TEST_METHOD(57) {
+ // It changes the user to the value of defaultUser.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) testConfig["nonexistant_uid"].asInt64(),
+ (gid_t) -1);
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(userNameForUid(uid), testConfig["default_user"].asString());
+ }
+
+ TEST_METHOD(58) {
+ // If 'group' is given, it changes group to the given group name.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) testConfig["nonexistant_uid"].asInt64(),
+ (gid_t) -1);
+ options.group = testConfig["normal_group_1"].asCString();
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_1"].asString());
+ }
+
+ TEST_METHOD(59) {
+ // If 'group' is set to the root group, it changes group to defaultGroup.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) testConfig["nonexistant_uid"].asInt64(),
+ (gid_t) -1);
+ string rootGroup = groupNameForGid(0);
+ options.group = rootGroup;
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["default_group"].asString());
+ }
+
+ // and 'group' is set to '!STARTUP_FILE!'
+ // and the startup file's group doesn't exist
+ TEST_METHOD(60) {
+ // It changes the group to the value given by defaultGroup.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) testConfig["nonexistant_uid"].asInt64(),
+ (gid_t) testConfig["nonexistant_gid"].asInt64());
+ options.group = "!STARTUP_FILE!";
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["default_group"].asString());
+ }
+
+ // and the startup file's group exists
+ TEST_METHOD(61) {
+ // It changes the group to the startup file's group.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) testConfig["nonexistant_uid"].asInt64(),
+ gidFor(testConfig["normal_group_1"].asString()));
+ options.group = "!STARTUP_FILE!";
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_1"].asString());
+ }
+
+ TEST_METHOD(62) {
+ // If the startup file is a symlink, then it uses the symlink's group, not the target's group.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) testConfig["nonexistant_uid"].asInt64(),
+ gidFor(testConfig["normal_group_2"].asString()));
+ chown("tmp.wsgi/passenger_wsgi.py.real",
+ (uid_t) -1,
+ gidFor(testConfig["normal_group_1"].asString()));
+ options.group = "!STARTUP_FILE!";
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), testConfig["normal_group_2"].asString());
+ }
+
+ TEST_METHOD(63) {
+ // If 'group' is not given, it changes the group to defaultUser's primary group.
+ SETUP_USER_SWITCHING_TEST();
+ lchown("tmp.wsgi/passenger_wsgi.py",
+ (uid_t) testConfig["nonexistant_uid"].asInt64(),
+ (gid_t) -1);
+ RUN_USER_SWITCHING_TEST();
+ ensure_equals(groupNameForGid(gid), primaryGroupFor(testConfig["default_user"].asString()));
+ }
+
+ TEST_METHOD(67) {
+ // It raises an error if it tries to lower to 'defaultUser',
+ // but that user doesn't exist.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = "root";
+ options.defaultUser = testConfig["nonexistant_user"].asCString();
+ try {
+ RUN_USER_SWITCHING_TEST();
+ fail();
+ } catch (const RuntimeException &e) {
+ ensure(containsSubstring(e.what(), "Cannot determine a user to lower privilege to"));
+ }
+ }
+
+ TEST_METHOD(68) {
+ // It raises an error if it tries to lower to 'default_group',
+ // but that group doesn't exist.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["normal_user_1"].asCString();
+ options.group = groupNameForGid(0);
+ options.defaultGroup = testConfig["nonexistant_group"].asCString();
+ try {
+ RUN_USER_SWITCHING_TEST();
+ fail();
+ } catch (const RuntimeException &e) {
+ ensure(containsSubstring(e.what(), "Cannot determine a group to lower privilege to"));
+ }
+ }
+
+ TEST_METHOD(69) {
+ // Changes supplementary groups to the owner's default supplementary groups.
+ SETUP_USER_SWITCHING_TEST();
+ options.user = testConfig["normal_user_1"].asCString();
+ RUN_USER_SWITCHING_TEST();
+ system(("groups " + testConfig["normal_user_1"].asString() + " > /tmp/info2.txt").c_str());
+ string defaultGroups = strip(readAll("/tmp/info2.txt"));
+ // default_groups.gsub!(/.*: */, '')
+ ensure_equals(groups, defaultGroups);
+ }
View
14 test/cxx/CxxTestMain.cpp
@@ -11,6 +11,8 @@
#include <MultiLibeio.cpp>
#include <Utils.h>
+#include <Utils/IOUtils.h>
+#include <Utils/json.h>
using namespace std;
@@ -92,6 +94,16 @@ doNothing(eio_req *req) {
return 0;
}
+static void
+loadConfigFile() {
+ Json::Reader reader;
+ if (!reader.parse(readAll("config.json"), testConfig)) {
+ fprintf(stderr, "Cannot parse config.json: %s\n",
+ reader.getFormattedErrorMessages().c_str());
+ exit(1);
+ }
+}
+
int
main(int argc, char *argv[]) {
signal(SIGPIPE, SIG_IGN);
@@ -119,6 +131,8 @@ main(int argc, char *argv[]) {
// Start an EIO thread to warm up Valgrind.
eio_nop(0, doNothing, NULL);
}
+
+ loadConfigFile();
bool all_ok = true;
if (runMode == RUN_ALL_GROUPS) {
View
3  test/cxx/TestSupport.cpp
@@ -8,10 +8,13 @@
#include <BackgroundEventLoop.cpp>
#include <Utils/IOUtils.h>
#include <Utils/ScopeGuard.h>
+#include <Utils/json.h>
namespace TestSupport {
ResourceLocator *resourceLocator = NULL;
+Json::Value testConfig;
+
void createServerInstanceDirAndGeneration(ServerInstanceDirPtr &serverInstanceDir,
ServerInstanceDir::GenerationPtr &generation)
View
2  test/cxx/TestSupport.h
@@ -24,6 +24,7 @@
#include "Exceptions.h"
#include "Utils.h"
#include "Utils/SystemTime.h"
+#include <Utils/json-forwards.h>
extern "C" {
struct ev_loop;
@@ -83,6 +84,7 @@ using namespace oxt;
extern ResourceLocator *resourceLocator;
+extern Json::Value testConfig;
/**
View
391 test/ruby/utils_spec.rb
@@ -68,397 +68,6 @@ module PhusionPassenger
end
end
- when_user_switching_possible do
- describe "#lower_privilege" do
- before :each do
- @options = {
- "default_user" => CONFIG["default_user"],
- "default_group" => CONFIG["default_group"]
- }
- @startup_file = "tmp.startup_file"
- @startup_file_target = "tmp.startup_file_target"
- File.symlink(@startup_file_target, @startup_file)
- File.touch(@startup_file_target)
- end
-
- after :each do
- File.unlink(@startup_file) rescue nil
- File.unlink(@startup_file_target) rescue nil
- end
-
- def run(options = {})
- script = %q{
- require 'phusion_passenger/utils'
- include PhusionPassenger::Utils
- options = Marshal.load(ARGV[0].unpack('m').first)
- startup_file = ARGV[1]
- begin
- lower_privilege(startup_file, options)
- puts "success"
- puts Process.uid
- puts Process.gid
- puts `groups`
- puts ENV["HOME"]
- puts ENV["USER"]
- rescue => e
- puts "error"
- puts e
- end
- }.strip
- data = Marshal.dump(@options.merge(options))
- output = run_script(script, [data].pack('m'), @startup_file)
- lines = output.split("\n")
- status = lines.shift
- if status == "success"
- @uid, @gid, @groups, @env_home, @env_user = lines
- @uid = @uid.to_i
- @gid = @gid.to_i
- @username = Etc.getpwuid(@uid).name
- @groupname = Etc.getgrgid(@gid).name
- else
- @error = lines[0]
- end
- end
-
- def primary_group_for(username)
- gid = Etc.getpwnam(username).gid
- return Etc.getgrgid(gid).name
- end
-
- def uid_for(username)
- return Etc.getpwnam(username).uid
- end
-
- def gid_for(group_name)
- return Etc.getgrnam(group_name).gid
- end
-
- def group_name_for_gid(gid)
- return Etc.getgrgid(gid).name
- end
-
- describe "if 'user' is given" do
- describe "and 'user' is 'root'" do
- before :each do
- @options["user"] = "root"
- end
-
- it "changes the user to the value of 'default_user'" do
- run
- @username.should == CONFIG["default_user"]
- end
-
- specify "if 'group' is given, it changes group to the given group name" do
- run("group" => CONFIG["normal_group_1"])
- @groupname.should == CONFIG["normal_group_1"]
- end
-
- specify "if 'group' is set to the root group, it changes group to default_group" do
- run("group" => group_name_for_gid(0))
- @groupname.should == CONFIG["default_group"]
- end
-
- describe "and 'group' is set to '!STARTUP_FILE!'" do
- before :each do
- @options["group"] = "!STARTUP_FILE!"
- end
-
- it "changes the group to the startup file's group" do
- File.lchown(-1,
- gid_for(CONFIG["normal_group_1"]),
- @startup_file)
- run
- @groupname.should == CONFIG["normal_group_1"]
- end
-
- specify "if the startup file is a symlink, then it uses the symlink's group, not the target's group" do
- File.lchown(-1,
- gid_for(CONFIG["normal_group_2"]),
- @startup_file)
- File.chown(-1,
- gid_for(CONFIG["normal_group_1"]),
- @startup_file_target)
- run
- @groupname.should == CONFIG["normal_group_2"]
- end
- end
-
- specify "if 'group' is not given, it changes the group to default_user's primary group" do
- run
- @groupname.should == primary_group_for(CONFIG["default_user"])
- end
- end
-
- describe "and 'user' is not 'root'" do
- before :each do
- @options["user"] = CONFIG["normal_user_1"]
- end
-
- it "changes the user to the given username" do
- run
- @username.should == CONFIG["normal_user_1"]
- end
-
- specify "if 'group' is given, it changes group to the given group name" do
- run("group" => CONFIG["normal_group_1"])
- @groupname.should == CONFIG["normal_group_1"]
- end
-
- specify "if 'group' is set to the root group, it changes group to default_group" do
- run("group" => group_name_for_gid(0))
- @groupname.should == CONFIG["default_group"]
- end
-
- describe "and 'group' is set to '!STARTUP_FILE!'" do
- before :each do
- @options["group"] = "!STARTUP_FILE!"
- end
-
- it "changes the group to the startup file's group" do
- File.lchown(-1,
- gid_for(CONFIG["normal_group_1"]),
- @startup_file)
- run
- @groupname.should == CONFIG["normal_group_1"]
- end
-
- specify "if the startup file is a symlink, then it uses the symlink's group, not the target's group" do
- File.lchown(-1,
- gid_for(CONFIG["normal_group_2"]),
- @startup_file)
- File.chown(-1,
- gid_for(CONFIG["normal_group_1"]),
- @startup_file_target)
- run
- @groupname.should == CONFIG["normal_group_2"]
- end
- end
-
- specify "if 'group' is not given, it changes the group to the user's primary group" do
- run
- @groupname.should == primary_group_for(CONFIG["normal_user_1"])
- end
- end
-
- describe "and the given username does not exist" do
- before :each do
- @options["user"] = CONFIG["nonexistant_user"]
- end
-
- it "changes the user to the value of 'default_user'" do
- run
- @username.should == CONFIG["default_user"]
- end
-
- specify "if 'group' is given, it changes group to the given group name" do
- run("group" => CONFIG["normal_group_1"])
- @groupname.should == CONFIG["normal_group_1"]
- end
-
- specify "if 'group' is set to the root group, it changes group to default_group" do
- run("group" => group_name_for_gid(0))
- @groupname.should == CONFIG["default_group"]
- end
-
- describe "and 'group' is set to '!STARTUP_FILE!'" do
- before :each do
- @options["group"] = "!STARTUP_FILE!"
- end
-
- it "changes the group to the startup file's group" do
- File.lchown(-1,
- gid_for(CONFIG["normal_group_1"]),
- @startup_file)
- run
- @groupname.should == CONFIG["normal_group_1"]
- end
-
- specify "if the startup file is a symlink, then it uses the symlink's group, not the target's group" do
- File.lchown(-1,
- gid_for(CONFIG["normal_group_2"]),
- @startup_file)
- File.chown(-1,
- gid_for(CONFIG["normal_group_1"]),
- @startup_file_target)
- run
- @groupname.should == CONFIG["normal_group_2"]
- end
- end
-
- specify "if 'group' is not given, it changes the group to default_user's primary group" do
- run
- @groupname.should == primary_group_for(CONFIG["default_user"])
- end
- end
- end
- describe "if 'user' is not given" do
- describe "and the startup file's owner exists" do
- before :each do
- File.lchown(uid_for(CONFIG["normal_user_1"]),
- -1,
- @startup_file)
- end
-
- it "changes the user to the owner of the startup file" do
- run
- @username.should == CONFIG["normal_user_1"]
- end
-
- specify "if the startup file is a symlink, then it uses the symlink's owner, not the target's owner" do
- File.lchown(uid_for(CONFIG["normal_user_2"]),
- -1,
- @startup_file)
- File.chown(uid_for(CONFIG["normal_user_1"]),
- -1,
- @startup_file_target)
- run
- @username.should == CONFIG["normal_user_2"]
- end
-
- specify "if 'group' is given, it changes group to the given group name" do
- run("group" => CONFIG["normal_group_1"])
- @groupname.should == CONFIG["normal_group_1"]
- end
-
- specify "if 'group' is set to the root group, it changes group to default_group" do
- run("group" => group_name_for_gid(0))
- @groupname.should == CONFIG["default_group"]
- end
-
- describe "and 'group' is set to '!STARTUP_FILE!'" do
- before :each do
- @options["group"] = "!STARTUP_FILE!"
- end
-
- it "changes the group to the startup file's group" do
- File.lchown(-1,
- gid_for(CONFIG["normal_group_1"]),
- @startup_file)
- run
- @groupname.should == CONFIG["normal_group_1"]
- end
-
- specify "if the startup file is a symlink, then it uses the symlink's group, not the target's group" do
- File.lchown(-1,
- gid_for(CONFIG["normal_group_2"]),
- @startup_file)
- File.chown(-1,
- gid_for(CONFIG["normal_group_1"]),
- @startup_file_target)
- run
- @groupname.should == CONFIG["normal_group_2"]
- end
- end
-
- specify "if 'group' is not given, it changes the group to the startup file's owner's primary group" do
- run
- @groupname.should == primary_group_for(CONFIG["normal_user_1"])
- end
- end
-
- describe "and the startup file's owner doesn't exist" do
- before :each do
- File.lchown(CONFIG["nonexistant_uid"],
- -1,
- @startup_file)
- end
-
- it "changes the user to the value of 'default_user'" do
- run
- @username.should == CONFIG["default_user"]
- end
-
- specify "if 'group' is given, it changes group to the given group name" do
- run("group" => CONFIG["normal_group_1"])
- @groupname.should == CONFIG["normal_group_1"]
- end
-
- specify "if 'group' is set to the root group, it changes group to default_group" do
- run("group" => group_name_for_gid(0))
- @groupname.should == CONFIG["default_group"]
- end
-
- describe "and 'group' is set to '!STARTUP_FILE!'" do
- before :each do
- @options["group"] = "!STARTUP_FILE!"
- end
-
- describe "and the startup file's group doesn't exist" do
- before :each do
- File.lchown(-1,
- CONFIG["nonexistant_gid"],
- @startup_file)
- end
-
- it "changes the group to the value given by 'default_group'" do
- run
- @groupname.should == CONFIG["default_group"]
- end
- end
-
- describe "and the startup file's group exists" do
- before :each do
- File.lchown(-1,
- gid_for(CONFIG["normal_group_1"]),
- @startup_file)
- end
-
- it "changes the group to the startup file's group" do
- run
- @groupname.should == CONFIG["normal_group_1"]
- end
-
- specify "if the startup file is a symlink, then it uses the symlink's group, not the target's group" do
- File.lchown(-1,
- gid_for(CONFIG["normal_group_2"]),
- @startup_file)
- File.chown(-1,
- gid_for(CONFIG["normal_group_1"]),
- @startup_file_target)
- run
- @groupname.should == CONFIG["normal_group_2"]
- end
- end
- end
-
- specify "if 'group' is not given, it changes the group to default_user's primary group" do
- run
- @groupname.should == primary_group_for(CONFIG["default_user"])
- end
- end
- end
-
- it "raises an error if it tries to lower to 'default_user', but that user doesn't exist" do
- run("user" => "root", "default_user" => CONFIG["nonexistant_user"])
- @error.should =~ /Cannot determine a user to lower privilege to/
- end
-
- it "raises an error if it tries to lower to 'default_group', but that group doesn't exist" do
- run("user" => CONFIG["normal_user_1"],
- "group" => group_name_for_gid(0),
- "default_group" => CONFIG["nonexistant_group"])
- @error.should =~ /Cannot determine a group to lower privilege to/
- end
-
- it "changes supplementary groups to the owner's default supplementary groups" do
- run("user" => CONFIG["normal_user_1"])
- default_groups = `groups "#{CONFIG['normal_user_1']}"`.strip
- default_groups.gsub!(/.*: */, '')
- @groups.should == default_groups
- end
-
- it "sets $HOME to the user's home directory" do
- run("user" => CONFIG["normal_user_1"])
- @env_home.should == Etc.getpwnam(CONFIG["normal_user_1"]).dir
- end
-
- it "sets $USER to the user's name" do
- run("user" => CONFIG["normal_user_1"])
- @env_user.should == CONFIG["normal_user_1"]
- end
- end
- end
-
######################
end
View
12 test/tut/tut.h
@@ -69,15 +69,9 @@ static int setenv(const char *name, const char *value, int override) {
typedef factory::object object; \
factory name## _group(#name)
-#ifdef __clang__
- #define TEST_METHOD(i) \
- template<> \
- void object::test<i>()
-#else
- #define TEST_METHOD(i) \
- template<> template<> \
- void object::test<i>()
-#endif
+#define TEST_METHOD(i) \
+ template<> template<> \
+ void object::test<i>()
/**
* Template Unit Tests Framework for C++.
Please sign in to comment.
Something went wrong with that request. Please try again.