forked from public/pyceo
Add mysql database stuff
This commit is contained in:
parent
827c17b107
commit
2552bc2243
|
@ -1,3 +1,5 @@
|
|||
/build-stamp
|
||||
/build
|
||||
*.pyc
|
||||
/build-ceo
|
||||
/build-ceod
|
||||
|
|
|
@ -91,6 +91,11 @@ def connect(auth_callback):
|
|||
if password == None:
|
||||
raise e
|
||||
|
||||
def connect_anonymous():
|
||||
"""Connect to LDAP."""
|
||||
|
||||
global ld
|
||||
ld = ldap.initialize(cfg['ldap_server_url'])
|
||||
|
||||
def disconnect():
|
||||
"""Disconnect from LDAP."""
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import os, re, subprocess, ldap, socket
|
||||
from ceo import conf, ldapi, terms, remote, ceo_pb2
|
||||
from ceo.excep import InvalidArgument
|
||||
|
||||
class MySQLException(Exception):
|
||||
pass
|
||||
|
||||
def create_mysql(username):
|
||||
try:
|
||||
request = ceo_pb2.AddMySQLUser()
|
||||
request.username = username
|
||||
|
||||
out = remote.run_remote('mysql', request.SerializeToString())
|
||||
|
||||
response = ceo_pb2.AddMySQLUserResponse()
|
||||
response.ParseFromString(out)
|
||||
|
||||
if any(message.status != 0 for message in response.messages):
|
||||
raise MySQLException('\n'.join(message.message for message in response.messages))
|
||||
|
||||
return response.password
|
||||
except remote.RemoteException, e:
|
||||
raise MySQLException(e)
|
||||
|
|
@ -1,15 +1,78 @@
|
|||
import urwid
|
||||
from ceo import members
|
||||
from ceo import members, mysql
|
||||
from ceo.urwid import search
|
||||
from ceo.urwid.widgets import *
|
||||
from ceo.urwid.window import *
|
||||
|
||||
def databases(menu):
|
||||
menu = make_menu([
|
||||
("Create MySQL database", create_mysql_db, None),
|
||||
("Back", raise_back, None),
|
||||
])
|
||||
push_window(menu, "Databases")
|
||||
class IntroPage(WizardPanel):
|
||||
def init_widgets(self):
|
||||
self.widgets = [
|
||||
urwid.Text("MySQL databases"),
|
||||
urwid.Divider(),
|
||||
urwid.Text("Members and hosted clubs may have one MySQL database each. You may "
|
||||
"create a database for an account if: \n"
|
||||
"\n"
|
||||
"- It is your personal account,\n"
|
||||
"- It is a club account, and you are in the club group, or\n"
|
||||
"- You are on the CSC systems committee\n"
|
||||
"\n"
|
||||
"You may also use this to reset your database password."
|
||||
)
|
||||
]
|
||||
def focusable(self):
|
||||
return False
|
||||
|
||||
def create_mysql_db(data):
|
||||
pass
|
||||
class UserPage(WizardPanel):
|
||||
def init_widgets(self):
|
||||
self.userid = LdapWordEdit(csclub_uri, csclub_base, 'uid',
|
||||
"Username: ")
|
||||
|
||||
self.widgets = [
|
||||
urwid.Text("Member Information"),
|
||||
urwid.Divider(),
|
||||
urwid.Text("Enter the user which will own the new database."),
|
||||
urwid.Divider(),
|
||||
self.userid,
|
||||
]
|
||||
def check(self):
|
||||
self.state['userid'] = self.userid.get_edit_text()
|
||||
self.state['member'] = None
|
||||
if self.state['userid']:
|
||||
self.state['member'] = members.get(self.userid.get_edit_text())
|
||||
if not self.state['member']:
|
||||
set_status("Member not found")
|
||||
self.focus_widget(self.userid)
|
||||
return True
|
||||
|
||||
class EndPage(WizardPanel):
|
||||
def init_widgets(self):
|
||||
self.headtext = urwid.Text("")
|
||||
self.midtext = urwid.Text("")
|
||||
|
||||
self.widgets = [
|
||||
self.headtext,
|
||||
urwid.Divider(),
|
||||
self.midtext,
|
||||
]
|
||||
def focusable(self):
|
||||
return False
|
||||
def activate(self):
|
||||
problem = None
|
||||
try:
|
||||
password = mysql.create_mysql(self.state['userid'])
|
||||
self.headtext.set_text("MySQL database created")
|
||||
self.midtext.set_text("Connection Information: \n"
|
||||
"\n"
|
||||
"Database: %s\n"
|
||||
"Username: %s\n"
|
||||
"Hostname: localhost\n"
|
||||
"Password: %s\n"
|
||||
"\n"
|
||||
"Note: Databases are only accessible from caffeine\n"
|
||||
% (self.state['userid'], self.state['userid'], password))
|
||||
except mysql.MySQLException, e:
|
||||
self.headtext.set_text("Failed to create MySQL database")
|
||||
self.midtext.set_text("We failed to create the database. The error was:\n\n%s" % e)
|
||||
|
||||
def check(self):
|
||||
pop_window()
|
||||
|
|
|
@ -143,6 +143,13 @@ def change_shell(data):
|
|||
shell.EndPage
|
||||
], (50, 20))
|
||||
|
||||
def create_mysql_db(data):
|
||||
push_wizard("Create MySQL database", [
|
||||
databases.IntroPage,
|
||||
databases.UserPage,
|
||||
databases.EndPage,
|
||||
], (60, 15))
|
||||
|
||||
def check_group(group):
|
||||
try:
|
||||
me = pwd.getpwuid(os.getuid()).pw_name
|
||||
|
@ -158,7 +165,6 @@ def top_menu():
|
|||
("Renew Club Rep", renew_club_user, None),
|
||||
("New Club", new_club, None),
|
||||
("Library", library.library, None),
|
||||
("Databases", databases.databases, None),
|
||||
]
|
||||
syscom_only = [
|
||||
("Manage Club or Group Members", manage_group, None),
|
||||
|
@ -168,8 +174,9 @@ def top_menu():
|
|||
]
|
||||
unrestricted = [
|
||||
("Display Member", display_member, None),
|
||||
("Change Shell", change_shell, None),
|
||||
("Search", search_members, None),
|
||||
("Change Shell", change_shell, None),
|
||||
("Create MySQL database", create_mysql_db, None),
|
||||
]
|
||||
footer = [
|
||||
("Exit", raise_abort, None),
|
||||
|
|
|
@ -29,7 +29,7 @@ Description: Computer Science Club Administrative Clients
|
|||
|
||||
Package: ceo-daemon
|
||||
Architecture: any
|
||||
Depends: ceo-python, ${shlibs:Depends}
|
||||
Depends: ceo-python, ${python:Depends}, ${shlibs:Depends}
|
||||
Description: Computer Science Club Administrative Daemon
|
||||
This package contains the CSC Electronic Office
|
||||
daemon.
|
||||
|
|
|
@ -4,7 +4,6 @@ CFLAGS := -g -O2 -fstack-protector-all -fPIE
|
|||
LDFLAGS := -pie -Wl,--as-needed
|
||||
|
||||
build:
|
||||
python setup.py -q build
|
||||
cd src && make CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
|
||||
|
||||
clean:
|
||||
|
@ -12,13 +11,17 @@ clean:
|
|||
dh_testroot
|
||||
dh_clean
|
||||
$(MAKE) -C src clean
|
||||
python setup.py -q clean -a
|
||||
python setup.py -q clean -a --build-base=build-ceo
|
||||
python setupd.py -q clean -a --build-base=build-ceod
|
||||
rm -rf build-ceo build-ceod
|
||||
|
||||
install: build
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_installdirs
|
||||
python setup.py -q install --no-compile -O0 --prefix=/usr --root=debian/ceo-python
|
||||
python setup.py -q build --build-base=build-ceo install --no-compile -O0 --prefix=/usr --root=debian/ceo-python
|
||||
python setupd.py -q build --build-base=build-ceod install --no-compile -O0 --prefix=/usr --root=debian/ceo-daemon \
|
||||
--install-scripts=/usr/lib/ceod
|
||||
$(MAKE) -C src DESTDIR=$(CURDIR)/debian/ceo-clients PREFIX=/usr install_clients
|
||||
$(MAKE) -C src DESTDIR=$(CURDIR)/debian/ceo-daemon PREFIX=/usr install_daemon
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
ginseng adduser 0x01
|
||||
ginseng adduser root 0x01
|
||||
|
|
|
@ -1 +1 @@
|
|||
ginseng mail 0x02
|
||||
ginseng mail root 0x02
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
caffeine mysql mysql 0x03
|
|
@ -35,6 +35,7 @@ all: $(BIN_PROGS) $(LIB_PROGS) $(EXT_PROGS) ../ceo/ceo_pb2.py
|
|||
|
||||
clean:
|
||||
rm -f $(BIN_PROGS) $(LIB_PROGS) $(EXT_PROGS) *.o ceo.pb-c.c ceo.pb-c.h
|
||||
rm -f ceo_pb2.py ../ceo/ceo_pb2.py
|
||||
|
||||
op-adduser.o addmember.o addclub.o: ceo.pb-c.h
|
||||
|
||||
|
|
|
@ -31,3 +31,12 @@ message UpdateMail {
|
|||
message UpdateMailResponse {
|
||||
repeated StatusMessage messages = 1;
|
||||
}
|
||||
|
||||
message AddMySQLUser {
|
||||
required string username = 1;
|
||||
}
|
||||
|
||||
message AddMySQLUserResponse {
|
||||
repeated StatusMessage messages = 1;
|
||||
optional string password = 2;
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ static void handle_op_message(uint32_t in_type, struct strbuf *in, struct strbuf
|
|||
"CEO_CONFIG_DIR", config_dir, NULL);
|
||||
char *argv[] = { op->path, NULL, };
|
||||
|
||||
if (spawnvem(op->path, argv, envp, in, out, 0))
|
||||
if (spawnvemu(op->path, argv, envp, in, out, 0, op->user))
|
||||
fatal("child %s failed", op->path);
|
||||
|
||||
if (!out->len)
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import os, sys, string, random, syslog, grp, errno, re
|
||||
from ceo import ceo_pb2, members, conf
|
||||
import MySQLdb
|
||||
|
||||
CONFIG_FILE = '/etc/csc/mysql.cf'
|
||||
|
||||
cfg = {}
|
||||
|
||||
def configure():
|
||||
string_fields = ['mysql_admin_username', 'mysql_admin_password']
|
||||
|
||||
# read configuration file
|
||||
cfg_tmp = conf.read(CONFIG_FILE)
|
||||
|
||||
# verify configuration
|
||||
conf.check_string_fields(CONFIG_FILE, string_fields, cfg_tmp)
|
||||
|
||||
# update the current configuration with the loaded values
|
||||
cfg.update(cfg_tmp)
|
||||
|
||||
def response_message(response, status, message):
|
||||
priority = syslog.LOG_ERR if status else syslog.LOG_INFO
|
||||
syslog.syslog(priority, message)
|
||||
msg = response.messages.add()
|
||||
msg.status = status
|
||||
msg.message = message
|
||||
return status
|
||||
|
||||
def random_password():
|
||||
chars = string.letters + string.digits
|
||||
return ''.join(random.choice(chars) for i in xrange(20))
|
||||
|
||||
def get_ceo_user():
|
||||
user = os.environ.get('CEO_USER')
|
||||
if not user:
|
||||
raise Exception("environment variable CEO_USER not set");
|
||||
return user
|
||||
|
||||
def check_group(user, group):
|
||||
try:
|
||||
return user in grp.getgrnam(group).gr_mem
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
def check_auth(remote_user, mysql_user, response):
|
||||
if remote_user == mysql_user:
|
||||
return response_message(response, 0, 'user %s creating database for self' % remote_user)
|
||||
club = members.get(mysql_user)
|
||||
if 'club' in club.get('objectClass', []):
|
||||
if check_group(remote_user, mysql_user):
|
||||
return response_message(response, 0, 'user %s is in club group %s' % (remote_user, mysql_user))
|
||||
else:
|
||||
return response_message(response, errno.EPERM, 'denied, user %s is not in club group %s' % (remote_user, mysql_user))
|
||||
if check_group(remote_user, 'syscom'):
|
||||
return response_message(response, 0, 'user %s is on systems committee' % remote_user)
|
||||
else:
|
||||
return response_message(response, errno.EPERM, 'denied, you may not create databases for other members')
|
||||
|
||||
def mysql_createdb(remote_user, mysql_user, response):
|
||||
if check_auth(remote_user, mysql_user, response):
|
||||
return
|
||||
|
||||
response.password = random_password()
|
||||
|
||||
if not re.match('^[a-zA-Z0-9-]+$', mysql_user):
|
||||
response_message(response, errno.EINVAL, 'invalid characters in username %s' % mysql_user)
|
||||
return
|
||||
|
||||
if not re.match('^[a-zA-Z0-9-]+$', response.password):
|
||||
response_message(response, errno.EINVAL, 'invalid characters in password %s' % response.password)
|
||||
return
|
||||
|
||||
try:
|
||||
connection = MySQLdb.Connect(user=cfg['mysql_admin_username'], passwd=cfg['mysql_admin_password'])
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("GRANT ALL PRIVILEGES ON `%s`.* TO `%s`@`localhost` IDENTIFIED BY '%s'"
|
||||
% (mysql_user, mysql_user, response.password))
|
||||
cursor.execute("CREATE DATABASE IF NOT EXISTS `%s`" % mysql_user)
|
||||
cursor.close()
|
||||
connection.close()
|
||||
|
||||
response_message(response, 0, 'successfully created database %s' % mysql_user)
|
||||
except MySQLdb.MySQLError, e:
|
||||
response_message(response, 1, 'exception occured creating database: %s' % e)
|
||||
|
||||
|
||||
def mysql_op():
|
||||
input = sys.stdin.read()
|
||||
|
||||
request = ceo_pb2.AddMySQLUser()
|
||||
request.ParseFromString(input)
|
||||
|
||||
remote_user = get_ceo_user()
|
||||
mysql_user = request.username
|
||||
|
||||
response = ceo_pb2.AddMySQLUserResponse()
|
||||
response_message(response, 0, 'mysql create db=%s by %s' % (mysql_user, remote_user))
|
||||
|
||||
mysql_createdb(remote_user, mysql_user, response)
|
||||
|
||||
sys.stdout.write(response.SerializeToString())
|
||||
|
||||
def main():
|
||||
configure()
|
||||
members.configure()
|
||||
members.connect_anonymous()
|
||||
syslog.openlog('op-mysql', syslog.LOG_PID, syslog.LOG_DAEMON)
|
||||
mysql_op()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
21
src/ops.c
21
src/ops.c
|
@ -3,6 +3,7 @@
|
|||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include "strbuf.h"
|
||||
#include "ops.h"
|
||||
|
@ -15,13 +16,14 @@ static struct op *ops;
|
|||
static const char *default_op_dir = "/usr/lib/ceod";
|
||||
static const char *op_dir;
|
||||
|
||||
static void add_op(char *host, char *name, uint32_t id) {
|
||||
static void add_op(char *host, char *name, char *user, uint32_t id) {
|
||||
struct op *new = xmalloc(sizeof(struct op));
|
||||
errno = 0;
|
||||
new->next = ops;
|
||||
new->name = xstrdup(name);
|
||||
new->id = id;
|
||||
new->path = NULL;
|
||||
new->user = xstrdup(user);
|
||||
|
||||
struct hostent *hostent = gethostbyname(host);
|
||||
if (!hostent)
|
||||
|
@ -35,11 +37,15 @@ static void add_op(char *host, char *name, uint32_t id) {
|
|||
sprintf(new->path, "%s/op-%s", op_dir, name);
|
||||
if (access(new->path, X_OK))
|
||||
fatalpe("cannot add op: %s: %s", name, new->path);
|
||||
|
||||
struct passwd *pw = getpwnam(user);
|
||||
if (!pw)
|
||||
fatalpe("cannot add op %s: getpwnam: %s", name, user);
|
||||
}
|
||||
|
||||
ops = new;
|
||||
debug("added op %s (%s%s)", new->name, new->local ? "" : "on ",
|
||||
new->local ? "local" : host);
|
||||
debug("added op %s (%s%s) [%s]", new->name, new->local ? "" : "on ",
|
||||
new->local ? "local" : host, new->user);
|
||||
}
|
||||
|
||||
struct op *get_local_op(uint32_t id) {
|
||||
|
@ -88,16 +94,16 @@ void setup_ops(void) {
|
|||
|
||||
struct strbuf **words = strbuf_splitws(&line);
|
||||
|
||||
if (strbuf_list_len(words) != 3)
|
||||
badconf("%s/%s: expected three words on line %d", op_config_dir, de->d_name, lineno);
|
||||
if (strbuf_list_len(words) != 4)
|
||||
badconf("%s/%s: expected four words on line %d", op_config_dir, de->d_name, lineno);
|
||||
|
||||
errno = 0;
|
||||
char *end;
|
||||
int id = strtol(words[2]->buf, &end, 0);
|
||||
int id = strtol(words[3]->buf, &end, 0);
|
||||
if (errno || *end)
|
||||
badconf("%s/%s: invalid id '%s' on line %d", op_config_dir, de->d_name, words[2]->buf, lineno);
|
||||
|
||||
add_op(words[0]->buf, words[1]->buf, id);
|
||||
add_op(words[0]->buf, words[1]->buf, words[2]->buf, id);
|
||||
op_count++;
|
||||
|
||||
strbuf_list_free(words);
|
||||
|
@ -115,6 +121,7 @@ void free_ops(void) {
|
|||
free(ops->name);
|
||||
free(ops->hostname);
|
||||
free(ops->path);
|
||||
free(ops->user);
|
||||
free(ops);
|
||||
ops = next;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ struct op {
|
|||
char *path;
|
||||
struct in_addr addr;
|
||||
struct op *next;
|
||||
char *user;
|
||||
};
|
||||
|
||||
void setup_ops(void);
|
||||
|
|
17
src/util.c
17
src/util.c
|
@ -8,6 +8,7 @@
|
|||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "strbuf.h"
|
||||
|
@ -173,6 +174,10 @@ void full_write(int fd, const void *buf, size_t count) {
|
|||
}
|
||||
|
||||
int spawnvem(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr) {
|
||||
return spawnvemu(path, argv, envp, output, input, cap_stderr, NULL);
|
||||
}
|
||||
|
||||
int spawnvemu(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr, char *user) {
|
||||
int pid, wpid, status;
|
||||
int tochild[2];
|
||||
int fmchild[2];
|
||||
|
@ -197,6 +202,18 @@ int spawnvem(const char *path, char *const *argv, char *const *envp, const struc
|
|||
close(tochild[1]);
|
||||
close(fmchild[0]);
|
||||
close(fmchild[1]);
|
||||
|
||||
if (user) {
|
||||
struct passwd *pw = getpwnam(user);
|
||||
if (!pw)
|
||||
fatalpe("getpwnam: %s", user);
|
||||
if (initgroups(user, pw->pw_gid))
|
||||
fatalpe("initgroups: %s", user);
|
||||
if (setregid(pw->pw_gid, pw->pw_gid))
|
||||
fatalpe("setregid: %s", user);
|
||||
if (setreuid(pw->pw_uid, pw->pw_uid))
|
||||
fatalpe("setreuid");
|
||||
}
|
||||
execve(path, argv, envp);
|
||||
fatalpe("execve");
|
||||
} else {
|
||||
|
|
|
@ -28,6 +28,7 @@ extern char **environ;
|
|||
int spawnv(const char *path, char *const *argv);
|
||||
int spawnv_msg(const char *path, char *const *argv, const struct strbuf *output);
|
||||
int spawnvem(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr);
|
||||
int spawnvemu(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr, char *user);
|
||||
void full_write(int fd, const void *buf, size_t count);
|
||||
ssize_t full_read(int fd, void *buf, size_t len);
|
||||
FILE *fopenat(DIR *d, const char *path, int flags);
|
||||
|
|
Loading…
Reference in New Issue