* Fixed security issue caused by sensitive property in req.data

* Fixed issue caused by setting a Metadata property to a Java value, e.g. java.net.URL instance becomes string without quotes. (Could this be a Rhino bug?)
 * Finalized conversion of AV_USER table to simple naming scheme ("user")
 * Added global getTitle() method which returns either site.title or root.sys_title
 * Implemented universal HopObject.value() method
 * Rededicated User.update() method since its previous functionality is now taken over by User.value()
 * Restructured login and register functionalities in User and MemberMgr
 * Replaced first occurrences of Exception with Error
 * Introduced i18n via gettext in User and MemberMgr
 * Removed getMessage() and Message in User and MemberMgr
 * Added first possible implementation of global getPermission() method
 * Modified code of global evalEmail() and evalURL() methods to work with Helma modules
 * Simplified global sendMail() method by not throwing any MailException anymore and returning the status code only
 * sendMail() now is using helma.Mail (so we can debug message output)
This commit is contained in:
Tobi Schäfer 2007-08-23 17:17:40 +00:00
parent 7d21e881e3
commit e9a7800a87
24 changed files with 401 additions and 436 deletions

View file

@ -117,7 +117,7 @@ File.prototype.url_macro = function(param) {
File.prototype.editlink_macro = function(param) {
if (session.user) {
try {
this.checkEdit(session.user, req.data.memberlevel);
this.checkEdit(session.user, res.data.memberlevel);
} catch (deny) {
return;
}
@ -133,7 +133,7 @@ File.prototype.editlink_macro = function(param) {
File.prototype.deletelink_macro = function(param) {
if (session.user) {
try {
this.checkEdit(session.user, req.data.memberlevel);
this.checkEdit(session.user, res.data.memberlevel);
} catch (deny) {
return;
}

View file

@ -44,8 +44,8 @@ site.foreign = SITE_ID
creator = object(User)
creator.local = FILE_F_USER_CREATOR
creator.foreign = USER_ID
creator.foreign = id
modifier = object(User)
modifier.local = FILE_F_USER_MODIFIER
modifier.foreign = USER_ID
modifier.foreign = id

View file

@ -154,7 +154,7 @@ FileMgr.prototype.deleteAll = function() {
FileMgr.prototype.checkAccess = function(action, usr, level) {
checkIfLoggedIn(this.href(req.action));
try {
this.checkAdd(session.user, req.data.memberlevel);
this.checkAdd(session.user, res.data.memberlevel);
} catch (deny) {
res.message = deny.toString();
res.redirect(this._parent.href());

View file

@ -23,12 +23,17 @@
//
app.addRepository("modules/core/HopObject.js");
app.addRepository("modules/helma/Mail.js");
app.addRepository("modules/helma/Search.js");
app.addRepository("modules/jala/code/Form.js");
app.addRepository("modules/jala/code/I18n.js");
app.addRepository("modules/jala/code/IndexManager.js");
app.addRepository("modules/jala/code/ListRenderer.js");
app.addRepository("modules/jala/code/Utilities.js");
var _ = gettext;
var search = new helma.Search();
/**
@ -131,6 +136,16 @@ function getDisplay(name) {
return name ? DISPLAY[name] : DISPLAY;
}
function getTitle() {
if (res.handlers.site) {
return res.handlers.site.title;
}
return root.getTitle();
}
function getPermission(role) {
return session.user && session.user.value("status") === role;
};
/**
* array containing short dateformats
@ -951,25 +966,25 @@ function scheduler() {
/**
* check if email-adress is syntactically correct
*/
function evalEmail(address) {
var m = new Mail();
m.addTo(address);
if (m.status != 0)
function evalEmail(str) {
if (!str.isEmail()) {
throw new Exception("emailInvalid");
return address;
}
return str;
}
/**
* function checks if url is correct
* if not it assumes that http is the protocol
*/
function evalURL(url) {
if (!url || url.contains("://") || url.contains("mailto:"))
return url;
if (url.contains("@"))
return "mailto:" + url;
else
return "http://" + url;
function evalURL(str) {
if (url = helma.Http.evalUrl(str)) {
return String(url);
} else if (str.contains("@")) {
return "mailto:" + str;
} else {
return "http://" + str;
}
}
/**
@ -1179,7 +1194,8 @@ function Exception(name, value) {
this.value = value;
this.toString = function() {
return getMessage("error." + this.name, this.value);
}
};
res.debug("Exception " + name + ": " + value);
return this;
}
@ -1464,44 +1480,25 @@ function extractContent(param, origContent) {
* @param String Body to use in email
* @return Obj Message object
*/
function sendMail(from, to, subject, body) {
if (!from || !to || !body)
throw new MailException("mailMissingParameters");
var mail = new Mail();
mail.setFrom(from ? from : root.sys_email);
if (to && to instanceof Array) {
for (var i in to)
mail.addBCC(to[i]);
} else
mail.addTo(to);
function sendMail(sender, recipient, subject, body) {
if (!sender || !recipient || !body) {
app.log("Insufficient arguments in method sendMail()");
return;
}
var mail = new helma.Mail();
mail.setFrom(sender);
if (recipient instanceof Array) {
for (var i in recipient) {
mail.addBCC(recipient[i]);
}
} else {
mail.addTo(recipient);
}
mail.setSubject(subject);
mail.setText(body);
switch (mail.status) {
case 10 :
throw new MailException("mailSubjectMissing");
break;
case 11 :
throw new MailException("mailTextMissing");
break;
case 12 :
throw new MailException("mailPartMissing");
break;
case 20 :
throw new MailException("mailToInvalid");
break;
case 21 :
throw new MailException("mailCCInvalid");
break;
case 22 :
throw new MailException("mailCCInvalid");
break;
case 30 :
throw new MailException("mailSend");
break;
}
// finally send the mail
// Add the message to the queue (method extension of helma.Mail)
mail.queue();
return new Message("mailSend");
return mail.status;
}
@ -1510,10 +1507,10 @@ function sendMail(from, to, subject, body) {
* that simply adds a mail object to an
* application-wide array (mail queue).
*/
Mail.prototype.queue = function() {
helma.Mail.prototype.queue = function() {
app.data.mailQueue.push(this);
return;
}
};
/**
@ -1609,7 +1606,6 @@ function rebuildIndexes() {
return;
}
function onCodeUpdate() {
var i, module, f;
for (i in app.modules) {
@ -1619,6 +1615,7 @@ function onCodeUpdate() {
}
return;
}
/**
* renders image element
* @param img Object contains the images's properties
@ -1639,7 +1636,6 @@ function renderImage(img, param) {
return;
}
/**
* function tries to check if the color contains just hex-characters
* if so, it returns the color-definition prefixed with a '#'

View file

@ -22,11 +22,22 @@
// $URL$
//
HopObject.prototype.value = function(key, value) {
if (value === undefined) {
HopObject.prototype.value = function(key, value, getter, setter) {
getter || (getter = function() {
return this[key];
});
setter || (setter = new Function);
if (value === undefined) {
if (key.constructor === Object) {
for (var i in key) {
this.value(i, key[i]);
}
return;
}
return getter();
}
return this[key] = value;
setter();
return;
};
/**
@ -193,6 +204,7 @@ HopObject.prototype.applyModuleMethod = function(module, funcName, param) {
HopObject.prototype.onCodeUpdate = function(prototype) {
return onCodeUpdate(prototype);
};
/**
* function checks if there's a site in path
* if true it checks if the site or the user is blocked
@ -202,7 +214,7 @@ HopObject.prototype.onRequest = function() {
res.redirect(app.data.redirectPostRequests);
autoLogin();
// defining skinpath, membershipLevel
req.data.memberlevel = null;
res.data.memberlevel = null;
// if root.sys_frontSite is set and the site is online
// we put it into res.handlers.site to ensure that the mirrored
// site works as expected
@ -212,7 +224,7 @@ HopObject.prototype.onRequest = function() {
if (res.handlers.site.blocked)
res.redirect(root.href("blocked"));
if (session.user)
req.data.memberlevel = res.handlers.site.members.getMembershipLevel(session.user);
res.data.memberlevel = res.handlers.site.members.getMembershipLevel(session.user);
// set a handler that contains the context
res.handlers.context = res.handlers.site;
} else {
@ -241,7 +253,7 @@ HopObject.prototype.onRequest = function() {
// check access, but only if user is *not* a sysadmin
// sysadmins are allowed to to everything
if (!session.user || !session.user.sysadmin)
this.checkAccess(req.action, session.user, req.data.memberlevel);
this.checkAccess(req.action, session.user, res.data.memberlevel);
return;
};

View file

@ -150,7 +150,7 @@ Image.prototype.editlink_macro = function(param) {
}
if (session.user) {
try {
this.checkEdit(session.user, req.data.memberlevel);
this.checkEdit(session.user, res.data.memberlevel);
} catch (deny) {
return;
}
@ -173,7 +173,7 @@ Image.prototype.deletelink_macro = function(param) {
}
if (session.user) {
try {
this.checkDelete(session.user, req.data.memberlevel);
this.checkDelete(session.user, res.data.memberlevel);
} catch (deny) {
return;
}
@ -228,7 +228,7 @@ Image.prototype.replacelink_macro = function(param) {
if (this.layout && path.Layout != this.layout) {
if (session.user) {
try {
path.Layout.images.checkAdd(session.user, req.data.memberlevel);
path.Layout.images.checkAdd(session.user, res.data.memberlevel);
} catch (deny) {
return;
}

View file

@ -53,11 +53,11 @@ thumbnail.foreign = IMAGE_ID
creator = object(User)
creator.local = IMAGE_F_USER_CREATOR
creator.foreign = USER_ID
creator.foreign = id
modifier = object(User)
modifier.local = IMAGE_F_USER_MODIFIER
modifier.foreign = USER_ID
modifier.foreign = id
tags = collection(TagHub)
tags.local.1 = $id

View file

@ -210,7 +210,7 @@ ImageMgr.prototype.deleteAll = function() {
ImageMgr.prototype.checkAccess = function(action, usr, level) {
checkIfLoggedIn(this.href(req.action));
try {
this.checkAdd(session.user, req.data.memberlevel);
this.checkAdd(session.user, res.data.memberlevel);
} catch (deny) {
res.message = deny.toString();
res.redirect(this._parent.href());

View file

@ -39,7 +39,7 @@ skins = mountpoint(SkinMgr)
images = mountpoint(ImageMgr)
preferences = mountpoint(Metadata)
preferences_data = LAYOUT_PREFERENCES_NEW
preferences_source = LAYOUT_PREFERENCES_NEW
site = object(Site)
site.local = LAYOUT_F_SITE
@ -51,11 +51,11 @@ parent.foreign = LAYOUT_ID
creator = object(User)
creator.local = LAYOUT_F_USER_CREATOR
creator.foreign = USER_ID
creator.foreign = id
modifier = object(User)
modifier.local = LAYOUT_F_USER_MODIFIER
modifier.foreign = USER_ID
modifier.foreign = id
sharedBy = collection(Layout)
sharedBy.local = LAYOUT_ID

View file

@ -215,7 +215,7 @@ LayoutMgr.prototype.renderParentLayoutChooser = function(selLayout, firstOption)
LayoutMgr.prototype.checkAccess = function(action, usr, level) {
checkIfLoggedIn(this.href(req.action));
try {
this.checkEdit(session.user, req.data.memberlevel);
this.checkEdit(session.user, res.data.memberlevel);
} catch (deny) {
res.message = deny.toString();
res.redirect(this._parent.href());

View file

@ -26,7 +26,167 @@
* main action, lists all members in alpabetical order
*/
MemberMgr.prototype.main_action = function() {
this.renderView(this, getMessage("User.role.members"));
this.renderView(this, gettext("Members"));
return;
};
/**
* register action
*/
MemberMgr.prototype.register_action = function() {
if (req.data.cancel) {
res.redirect(this._parent.href());
} else if (req.data.register) {
try {
var user = User.register(req.data);
// Subscribe user to this site if public
if (res.handlers.site && res.handlers.site.online) {
this.add(new Membership(user));
}
var title = getTitle();
if (root.sys_email) {
var sp = {name: user.name};
sendMail(root.sys_email, user.email,
gettext('Welcome to "{0}"!', title),
this.renderSkinAsString("mailregconfirm", sp));
}
var url = session.data.referrer || this._parent.href();
delete session.data.referrer;
res.message = gettext('Welcome to "{0}", {1}. Have fun!',
title, user.name);
res.redirect(url);
} catch (ex) {
app.log(ex);
res.message = ex;
}
}
session.data.token = User.getSalt();
res.data.action = this.href(req.action);
res.data.title = gettext("Login");
res.data.body = this.renderSkinAsString("register");
this._parent.renderSkin("page");
return;
};
/**
* login action
*/
MemberMgr.prototype.login_action = function() {
if (req.data.login) {
try {
var user = User.login(req.data);
var url = session.data.referrer || this._parent.href();
delete session.data.referrer;
res.message = gettext('Welcome to "{0}", {1}. Have fun!',
res.handlers.context.getTitle(), user.name);
res.redirect(url);
} catch (ex) {
res.message = ex;
app.log(ex);
}
}
if (!session.data.referrer) {
session.data.referrer = req.data.http_referer;
}
session.data.token = User.getSalt();
res.data.action = this.href(req.action);
res.data.title = gettext("Login");
res.data.body = this.renderSkinAsString("login");
this._parent.renderSkin("page");
return;
};
/**
* logout action
*/
MemberMgr.prototype.logout_action = function() {
if (session.user) {
res.message = gettext("Good bye, {0}! Lookin' forward to seeing you again!",
session.user.name);
session.logout();
delete session.data.referrer;
res.setCookie("avUsr", "");
res.setCookie("avPw", "");
}
res.redirect(this._parent.href());
return;
};
/**
* action for creating a new Membership
*/
MemberMgr.prototype.create_action = function() {
if (req.data.cancel) {
res.redirect(this.href());
} else if (req.data.keyword) {
try {
var result = this.searchUser(req.data.keyword);
res.message = result.toString();
res.data.searchresult = this.renderSkinAsString("searchresult",
{result: result.obj});
} catch (err) {
res.message = err.toString();
}
} else if (req.data.add) {
try {
var result = this.evalNewMembership(req.data.username, session.user);
res.message = result.toString();
// send confirmation mail
var sp = new Object();
sp.site = result.obj.site.title;
sp.creator = session.user.name;
sp.url = result.obj.site.href();
sp.account = result.obj.user.name;
var mailbody = this.renderSkinAsString("mailnewmember", sp);
sendMail(root.sys_email, result.obj.user.email,
getMessage("mail.newMember", result.obj.site.title), mailbody);
res.redirect(result.obj.href("edit"));
} catch (err) {
res.message = err.toString();
if (err instanceof MailException)
res.redirect(result.obj.href("edit"));
res.redirect(this.href());
}
}
res.data.action = this.href(req.action);
res.data.title = getMessage("MemberMgr.searchTitle",
{siteTitle: this._parent.title});
res.data.body = this.renderSkinAsString("new");
res.handlers.context.renderSkin("page");
return;
};
/**
* edit actions for user profiles
*/
MemberMgr.prototype.edit_action = function() {
if (req.data.cancel) {
res.redirect(this._parent.href());
} else if (req.data.save) {
try {
session.user.update(req.data);
res.message = gettext("The changes were saved successfully.");
res.redirect(this._parent.href());
} catch (err) {
res.message = err.toString();
}
}
session.data.token = User.getSalt();
session.data.salt = session.user.value("salt"); // FIXME
res.data.title = gettext("Profile of user {0}", session.user.name);
res.data.body = session.user.renderSkinAsString("edit");
this._parent.renderSkin("page");
return;
};
MemberMgr.prototype.salt_js_action = function() {
var user;
if (user = User.getByName(req.data.user)) {
res.write((user.value("salt") || String.EMPTY).toSource());
}
return;
};
@ -34,7 +194,7 @@ MemberMgr.prototype.main_action = function() {
* list all subscribers of a site
*/
MemberMgr.prototype.subscribers_action = function() {
this.renderView(this.subscribers, getMessage("User.role.subscribers"));
this.renderView(this.subscribers, gettext("Subscribers"));
return;
};
@ -42,7 +202,7 @@ MemberMgr.prototype.subscribers_action = function() {
* list all contributors of a site
*/
MemberMgr.prototype.contributors_action = function() {
this.renderView(this.contributors, getMessage("User.role.contributors"));
this.renderView(this.contributors, gettext("Contributors"));
return;
};
@ -50,7 +210,7 @@ MemberMgr.prototype.contributors_action = function() {
* list all content managers of a site
*/
MemberMgr.prototype.managers_action = function() {
this.renderView(this.managers, getMessage("User.role.contentManagers"));
this.renderView(this.managers, gettext("Content Managers"));
return;
};
@ -58,7 +218,7 @@ MemberMgr.prototype.managers_action = function() {
* list all admins of a site
*/
MemberMgr.prototype.admins_action = function() {
this.renderView(this.admins, getMessage("User.role.administrators"));
this.renderView(this.admins, gettext("Administrators"));
return;
};
@ -67,7 +227,7 @@ MemberMgr.prototype.admins_action = function() {
* site list of a user's subscriptions
*/
MemberMgr.prototype.updated_action = function() {
res.data.title = getMessage("MemberMgr.updateTitle", {userName: session.user.name});
res.data.title = gettext("Updated sites for user {0}", session.user.name);
res.data.sitelist = session.user.renderSkinAsString("sitelist");
res.data.body = session.user.renderSkinAsString("subscriptions");
res.handlers.context.renderSkin("page");
@ -90,209 +250,15 @@ MemberMgr.prototype.memberships_action = function() {
return;
};
/**
* action for creating a new Membership
*/
MemberMgr.prototype.create_action = function() {
if (req.data.cancel)
res.redirect(this.href());
else if (req.data.keyword) {
try {
var result = this.searchUser(req.data.keyword);
res.message = result.toString();
res.data.searchresult = this.renderSkinAsString("searchresult", {result: result.obj});
} catch (err) {
res.message = err.toString();
}
} else if (req.data.add) {
try {
var result = this.evalNewMembership(req.data.username, session.user);
res.message = result.toString();
// send confirmation mail
var sp = new Object();
sp.site = result.obj.site.title;
sp.creator = session.user.name;
sp.url = result.obj.site.href();
sp.account = result.obj.user.name;
var mailbody = this.renderSkinAsString("mailnewmember", sp);
sendMail(root.sys_email,
result.obj.user.email,
getMessage("mail.newMember", result.obj.site.title),
mailbody);
res.redirect(result.obj.href("edit"));
} catch (err) {
res.message = err.toString();
if (err instanceof MailException)
res.redirect(result.obj.href("edit"));
res.redirect(this.href());
}
}
res.data.action = this.href(req.action);
res.data.title = getMessage("MemberMgr.searchTitle", {siteTitle: this._parent.title});
res.data.body = this.renderSkinAsString("new");
res.handlers.context.renderSkin("page");
return;
};
/**
* edit actions for user profiles
*/
MemberMgr.prototype.edit_action = function() {
if (req.data.cancel) {
res.redirect(this._parent.href());
} else if (req.data.save) {
try {
res.message = this.updateUser(req.data);
res.redirect(this._parent.href());
} catch (err) {
res.message = err.toString();
}
}
session.data.token = User.getSalt();
session.data.salt = session.user.value("salt"); // FIXME
res.data.title = getMessage("MemberMgr.editProfileTitle", {userName: session.user.name});
res.data.body = session.user.renderSkinAsString("edit");
this._parent.renderSkin("page");
return;
};
/**
* login action
*/
MemberMgr.prototype.login_action = function() {
res.message = new Message("introLogin");
if (req.data.login) {
//try {
var user = User.getByName(req.data.name);
if (!user) {
throw new Exception("loginTypo");
}
res.message = user.login(req.data);
if (session.data.referrer) {
var url = session.data.referrer;
session.data.referrer = null;
} else {
var url = this._parent.href();
}
res.redirect(url);
//} catch (err) {
// res.message = err.toString();
//}
}
if (!session.data.referrer && req.data.http_referer) {
session.data.referrer = req.data.http_referer;
}
session.data.token = User.getSalt();
res.data.action = this.href(req.action);
res.data.title = getMessage("User.loginTitle");
res.data.body = this.renderSkinAsString("login");
this._parent.renderSkin("page");
return;
};
/**
* logout action
*/
MemberMgr.prototype.logout_action = function() {
if (session.user) {
res.message = new Message("logout", session.user.name);
session.logout();
session.data.referrer = null;
res.setCookie ("avUsr", "");
res.setCookie ("avPw", "");
}
res.redirect(this._parent.href());
return;
};
/**
* register action
*/
MemberMgr.prototype.register_action = function() {
if (req.data.cancel) {
res.redirect(this._parent.href());
} else if (req.data.register) {
if (session.data.referrer) {
var url = session.data.referrer;
session.data.referrer = null;
} else {
var url = this._parent.href();
}
try {
var result = User.register(req.data);
// Subscribe user to this site if public
if (path.site && path.site.online) {
this.add(new Membership(result.obj));
}
res.message = result.toString();
// now we log in the user and send the confirmation mail
session.login(result.obj);
if (root.sys_email) {
var sp = {name: result.obj.name, password: result.obj.password};
sendMail(root.sys_email,
result.obj.email,
getMessage("mail.registration", root.getTitle()),
this.renderSkinAsString("mailregconfirm", sp)
);
}
res.redirect(url);
} catch (err) {
res.message = err.toString();
// if we got a mail exception redirect back
if (err instanceof MailException)
res.redirect(url);
}
}
session.data.token = User.getSalt();
res.data.action = this.href(req.action);
res.data.title = getMessage("User.registerTitle");
res.data.body = this.renderSkinAsString("register");
this._parent.renderSkin("page");
return;
};
/**
* password reminder action
*/
MemberMgr.prototype.sendpwd_action = function() {
if (req.data.cancel)
res.redirect(this._parent.href());
else if (req.data.send) {
try {
res.message = this.sendPwd(req.data.email);
res.redirect(this._parent.href());
} catch (err) {
res.message = err.toString();
}
}
res.data.action = this.href(req.action);
res.data.title = getMessage("User.recoverPasswordTitle");
res.data.body = this.renderSkinAsString("sendpwd");
this._parent.renderSkin("page");
return;
};
MemberMgr.prototype.salt_js_action = function() {
var user;
if (user = User.getByName(req.data.user)) {
res.write((user.value("salt") || String.EMPTY).toSource());
}
return;
};
/**
* macro renders a link to signup if user is not member of this site
* if user is member, it displays the level of membership
*/
MemberMgr.prototype.membership_macro = function(param) {
if (req.data.memberlevel == null)
if (res.data.memberlevel == null) {
return;
res.write(getRole(req.data.memberlevel));
}
res.write(getRole(res.data.memberlevel));
return;
};
@ -302,9 +268,10 @@ MemberMgr.prototype.membership_macro = function(param) {
* and the site is public
*/
MemberMgr.prototype.subscribelink_macro = function(param) {
if (this._parent.online && req.data.memberlevel == null)
if (this._parent.online && res.data.memberlevel == null) {
Html.link({href: this._parent.href("subscribe")},
param.text ? param.text : getMessage("MemberMgr.signUp"));
param.text ? param.text : getMessage("MemberMgr.signUp"));
}
return;
};
@ -314,9 +281,10 @@ MemberMgr.prototype.subscribelink_macro = function(param) {
*/
MemberMgr.prototype.subscriptionslink_macro = function(param) {
if (session.user.size())
if (session.user.size()) {
Html.link({href: this.href("updated")},
param.text ? param.text : getMessage("MemberMgr.subscriptions"));
param.text ? param.text : getMessage("MemberMgr.subscriptions"));
}
return;
};
@ -389,46 +357,6 @@ MemberMgr.prototype.modSoruaLoginForm_action = function() {
this.renderSkin("modSorua");
};
/**
* update user-profile
* @param Obj Object containing form values
* @return Obj Object containing two properties:
* - error (boolean): true if error happened, false if everything went fine
* - message (String): containing a message to user
*/
MemberMgr.prototype.updateUser = function(data) {
var user = session.user;
if (!data.digest && data.password) {
data.digest = ((data.password + user.value("salt")).md5() +
session.data.token).md5();
}
if (data.digest) {
if (data.digest !== user.getDigest(session.data.token)) {
throw new Exception("accountOldPwd");
}
if (!data.hash) {
if (!data.newPassword || !data.newPasswordConfirm) {
throw new Exception("accountNewPwdMissing");
} else if (data.newPassword !== data.newPasswordConfirm) {
throw new Exception("passwordNoMatch");
}
data.hash = (data.newPassword + session.data.token).md5();
}
user.update({
hash: data.hash,
salt: session.data.token
});
}
user.update({
url: evalURL(data.url),
email: evalEmail(data.email),
});
return new Message("update");
};
/**
* function retrieves a list of usernames/passwords for a submitted email-address
* and sends them as mail
@ -625,6 +553,7 @@ MemberMgr.prototype.renderSubscriptionView = function(collection, title) {
res.handlers.context.renderSkin("page");
return;
};
/**
* permission check (called by hopobject.onRequest())
* @param String name of action
@ -642,15 +571,16 @@ MemberMgr.prototype.checkAccess = function(action, usr, level) {
case "contributors" :
case "subscribers" :
case "create" :
checkIfLoggedIn(this.href(action));
this.checkEditMembers(usr, level);
break;
checkIfLoggedIn(this.href(action));
this.checkEditMembers(usr, level);
break;
case "updated" :
case "memberships" :
case "subscriptions" :
case "edit" :
checkIfLoggedIn(this.href(action));
break;
checkIfLoggedIn(this.href(action));
break;
}
} catch (deny) {
res.message = deny.toString();

View file

@ -23,34 +23,36 @@ $(function() {
<input type="hidden" name="digest" id="digest" />
<table border="0" cellspacing="0" cellpadding="3">
<tr>
<td class="small" nowrap="nowrap">Username:</td>
<td class="small">Username:</td>
<td nowrap="nowrap">
<input type="text" name="name" id="name" size="15" tabindex="1" />
<input type="text" name="name" id="name" size="15" tabindex="1"
value="<% request.name %>" />
</td>
<td rowspan="4">&nbsp;</td>
<td rowspan="4" nowrap="nowrap"> </td>
<td class="small">
<% membermgr.link to="register" text="Not registered yet?" %>
<a href="<% membermgr.href action="register" %>">Not registered yet?</a>
</td>
</tr>
<tr>
<td class="small" nowrap="nowrap">Password:</td>
<td nowrap="nowrap">
<td>
<input type="password" name="password" id="password" size="15" tabindex="2" />
</td>
<td class="small">
<% membermgr.link to="sendpwd" text="Forgot your password?" %>
<a href="<% membermgr.href action="sendpwd" %>">Forgot your password?</a>
</td>
</tr>
<tr>
<td nowrap="nowrap">&nbsp;</td>
<td colspan="2" class="small" nowrap="nowrap">
<input type="checkbox" id="remember" name="remember" tabindex="3" />
<td nowrap="nowrap"> </td>
<td colspan="2" class="small">
<input type="checkbox" id="remember" name="remember" tabindex="3"
<% if <% request.remember %> is "on" then 'checked="checked"' %> />
<label for="remember">Remember me</label>
</td>
</tr>
<tr>
<td nowrap="nowrap">&nbsp;</td>
<td colspan="2" nowrap="nowrap"><br />
<td nowrap="nowrap"> </td>
<td colspan="2"><br />
<button type="submit" id="submit" name="login" value="login"
tabindex="4">Login</button>
</td>

View file

@ -22,6 +22,17 @@
// $URL$
//
/**
* constructor function for membership objects
*/
Membership.prototype.constructor = function(usr, level) {
this.user = usr;
this.username = usr.name;
this.level = level ? level : SUBSCRIBER;
this.createtime = new Date();
return this;
};
/**
* edit action
*/
@ -81,10 +92,9 @@ Membership.prototype.mailto_action = function() {
if (req.data.text) {
try {
var mailbody = this.renderSkinAsString("mailmessage", {text: req.data.text});
res.message = sendMail(session.user.email,
this.user.email,
getMessage("mail.toUser", root.sys_title),
mailbody);
sendMail(session.user.email, this.user.email,
getMessage("mail.toUser", root.sys_title), mailbody);
res.message = new Message("mailSend");
res.redirect(this._parent.href());
} catch (err) {
res.message = err.toString();
@ -100,6 +110,7 @@ Membership.prototype.mailto_action = function() {
this.site.renderSkin("page");
return;
};
/**
* macro renders the username
*/
@ -111,7 +122,6 @@ Membership.prototype.username_macro = function(param) {
return;
};
/**
* macro renders e-mail address
*/
@ -137,7 +147,6 @@ Membership.prototype.url_macro = function(param) {
/**
* macro renders user-level
*/
Membership.prototype.level_macro = function(param) {
if (param.as == "editor")
Html.dropDown({name: "level"}, getRoles(), this.level, param.firstOption);
@ -155,7 +164,6 @@ Membership.prototype.editlink_macro = function(param) {
return;
};
/**
* macro renders a link for deleting a membership
*/
@ -175,17 +183,6 @@ Membership.prototype.unsubscribelink_macro = function(param) {
param.text ? param.text : getMessage("Membership.unsubscribe"));
return;
};
/**
* constructor function for membership objects
*/
Membership.prototype.constructor = function(usr, level) {
this.user = usr;
this.username = usr.name;
this.level = level ? level : SUBSCRIBER;
this.createtime = new Date();
return this;
};
/**
* function updates a membership
@ -195,7 +192,6 @@ Membership.prototype.constructor = function(usr, level) {
* - error (boolean): true if error happened, false if everything went fine
* - message (String): containing a message to user
*/
Membership.prototype.updateMembership = function(lvl, modifier) {
if (isNaN(lvl))
throw new Exception("memberNoRole");
@ -206,14 +202,13 @@ Membership.prototype.updateMembership = function(lvl, modifier) {
this.level = lvl;
this.modifier = modifier;
this.modifytime = new Date();
sendMail(root.sys_email,
this.user.email,
getMessage("mail.statusChange", this.site.title),
this.renderSkinAsString("mailstatuschange")
);
sendMail(root.sys_email, this.user.email,
getMessage("mail.statusChange", this.site.title),
this.renderSkinAsString("mailstatuschange"));
}
return new Message("update");
};
/**
* permission check (called by hopobject.onRequest())
* @param String name of action
@ -231,4 +226,3 @@ Membership.prototype.checkAccess = function(action, usr, level) {
}
return;
};

View file

@ -98,6 +98,10 @@ Metadata.prototype.getData = function() {
*/
Metadata.prototype.set = function(key, value) {
if (arguments.length > 1) {
// Coerce Java classes into String prototypes
if (!value.constructor) {
value = String(value);
}
this.get()[key] = value;
} else if (arguments.length > 0) {
value = arguments[0];

View file

@ -219,7 +219,7 @@ Poll.prototype.total_macro = function(param) {
Poll.prototype.editlink_macro = function(param) {
if (session.user) {
try {
this.checkEdit(session.user, req.data.memberlevel);
this.checkEdit(session.user, res.data.memberlevel);
} catch (deny) {
return;
}
@ -237,7 +237,7 @@ Poll.prototype.editlink_macro = function(param) {
Poll.prototype.deletelink_macro = function(param) {
if (session.user) {
try {
this.checkDelete(session.user, req.data.memberlevel);
this.checkDelete(session.user, res.data.memberlevel);
} catch (deny) {
return;
}
@ -254,7 +254,7 @@ Poll.prototype.deletelink_macro = function(param) {
Poll.prototype.viewlink_macro = function(param) {
try {
if (!this.closed) {
this.checkVote(session.user, req.data.memberlevel);
this.checkVote(session.user, res.data.memberlevel);
Html.link({href: this.href()},
param.text ? param.text : getMessage("Poll.vote"));
}
@ -271,7 +271,7 @@ Poll.prototype.viewlink_macro = function(param) {
Poll.prototype.closelink_macro = function(param) {
if (session.user) {
try {
this.checkDelete(session.user, req.data.memberlevel);
this.checkDelete(session.user, res.data.memberlevel);
} catch (deny) {
return;
}
@ -339,7 +339,7 @@ Poll.prototype.evalPoll = function(question, choices, creator) {
* - url (String): the URL string of the poll
*/
Poll.prototype.evalVote = function(param, usr) {
this.checkVote(usr, req.data.memberlevel);
this.checkVote(usr, res.data.memberlevel);
if (!param.choice)
throw new Exception("noVote");
var c = this.get(param.choice);

View file

@ -38,11 +38,11 @@ site.foreign = SITE_ID
creator = object(User)
creator.local = POLL_F_USER_CREATOR
creator.foreign = USER_ID
creator.foreign = 1
modifier = object(User)
modifier.local = POLL_F_USER_MODIFIER
modifier.foreign = USER_ID
modifier.foreign = ID
_children = collection(Choice)
_children.local = POLL_ID

View file

@ -785,9 +785,7 @@ Root.prototype.getLocale = function() {
* if not, it returns "Antville"
*/
Root.prototype.getTitle = function() {
if (!root.sys_title)
return "antville";
return root.sys_title;
return root.sys_title || "Antville";
};
/**

View file

@ -299,7 +299,7 @@ Site.prototype.mostread_action = function() {
Site.prototype.referrers_action = function() {
if (req.data.permanent && session.user) {
try {
this.checkEdit(session.user, req.data.memberlevel);
this.checkEdit(session.user, res.data.memberlevel);
} catch (err) {
res.message = err.toString();
res.redirect(this.href());
@ -716,11 +716,11 @@ Site.prototype.navigation_macro = function(param) {
case "contributors" :
if (session.user.sysadmin ||
this.preferences.get("usercontrib") ||
req.data.memberlevel >= CONTRIBUTOR)
res.data.memberlevel >= CONTRIBUTOR)
this.renderSkin("contribnavigation");
break;
case "admins" :
if (session.user.sysadmin || req.data.memberlevel >= ADMIN)
if (session.user.sysadmin || res.data.memberlevel >= ADMIN)
this.renderSkin("adminnavigation");
break;
}
@ -864,7 +864,7 @@ Site.prototype.age_macro = function(param) {
*/
Site.prototype.history_macro = function(param) {
try {
this.checkView(session.user, req.data.memberlevel);
this.checkView(session.user, res.data.memberlevel);
} catch (deny) {
return;
}
@ -1111,7 +1111,7 @@ Site.prototype.switch_macro = function(param) {
try {
// FIXME: unfortunately, the check* methods are
// not very handy, anymore... (need try/catch block)
this.checkEdit(session.user, req.data.memberlevel);
this.checkEdit(session.user, res.data.memberlevel);
res.write(param.on);
} catch (err) {
res.write(param.off);

View file

@ -240,7 +240,7 @@ Story.prototype.createtime_macro = function(param) {
Story.prototype.editlink_macro = function(param) {
if (session.user) {
try {
this.checkEdit(session.user, req.data.memberlevel);
this.checkEdit(session.user, res.data.memberlevel);
} catch (deny) {
return;
}
@ -261,7 +261,7 @@ Story.prototype.editlink_macro = function(param) {
Story.prototype.deletelink_macro = function(param) {
if (session.user) {
try {
this.checkDelete(session.user, req.data.memberlevel);
this.checkDelete(session.user, res.data.memberlevel);
} catch (deny) {
return;
}
@ -282,7 +282,7 @@ Story.prototype.deletelink_macro = function(param) {
Story.prototype.onlinelink_macro = function(param) {
if (session.user) {
try {
this.checkEdit(session.user, req.data.memberlevel);
this.checkEdit(session.user, res.data.memberlevel);
} catch (deny) {
return;
}
@ -313,7 +313,7 @@ Story.prototype.onlinelink_macro = function(param) {
Story.prototype.viewlink_macro = function(param) {
if (session.user) {
try {
this.checkView(session.user, req.data.memberlevel);
this.checkView(session.user, res.data.memberlevel);
} catch (deny) {
return;
}

View file

@ -33,4 +33,4 @@ createtime = SYSLOG_CREATETIME
sysadmin = object(User)
sysadmin.local = SYSLOG_F_USER_CREATOR
sysadmin.foreign = USER_ID
sysadmin.foreign = id

View file

@ -82,7 +82,7 @@ Tag.prototype.permission_macro = function(param, type) {
Tag.prototype.getPermission = function(type) {
var user = session.user;
var level = req.data.memberlevel;
var level = res.data.memberlevel;
switch (type) {
case "main":
return true;

View file

@ -24,7 +24,7 @@
User.prototype.constructor = function(data) {
var now = new Date;
this.update({
this.value({
name: data.name,
hash: data.hash,
salt: session.data.token,
@ -39,7 +39,7 @@ User.prototype.constructor = function(data) {
User.prototype.value = function(key, value) {
var self = this;
var getValue = function() {
var getter = function() {
switch (key) {
case "hash":
case "salt":
@ -51,7 +51,7 @@ User.prototype.value = function(key, value) {
}
};
var setValue = function() {
var setter = function() {
switch (key) {
case "email":
case "lastVisit":
@ -65,14 +65,7 @@ User.prototype.value = function(key, value) {
}
};
return value === undefined ? getValue() : setValue();
};
User.prototype.update = function(values) {
for (var key in values) {
this.value(key, values[key]);
}
return;
return HopObject.prototype.value.call(this, key, value, getter, setter);
};
User.prototype.touch = function() {
@ -80,41 +73,52 @@ User.prototype.touch = function() {
return;
};
User.prototype.login = function(data) {
var user = this;
var digest = data.digest;
// Calculate digest for JavaScript-disabled browsers
if (!digest) {
digest = ((data.password + this.value("salt")).md5() +
session.data.token).md5();
}
// Check if login is correct
if (digest !== this.getDigest(session.data.token)) {
throw new Exception("loginTypo");
}
session.login(user);
user.touch();
if (data.remember) {
// Set long running cookies for automatic login
res.setCookie("avUsr", user.value("name"), 365);
var ip = req.data.http_remotehost.clip(getProperty("cookieLevel", "4"),
"", "\\.");
res.setCookie("avPw", (user.value("hash") + ip).md5(), 365);
}
return new Message("welcome", [res.handlers.context.getTitle(), user.name]);
};
User.prototype.getDigest = function(token) {
token || (token = String.EMPTY);
return (this.value("hash") + token).md5();
};
/**
* update user-profile
* @param Obj Object containing form values
* @return Obj Object containing two properties:
* - error (boolean): true if error happened, false if everything went fine
* - message (String): containing a message to user
*/
User.prototype.update = function(data) {
if (!data.digest && data.password) {
data.digest = ((data.password + this.value("salt")).md5() +
session.data.token).md5();
}
if (data.digest) {
if (data.digest !== this.getDigest(session.data.token)) {
throw Error(gettext("Oops, your old password is incorrect. Please re-enter it."));
}
if (!data.hash) {
if (!data.newPassword || !data.newPasswordConfirm) {
throw Error(gettext("Please specify a new password."));
} else if (data.newPassword !== data.newPasswordConfirm) {
throw Error(gettext("Unfortunately, your passwords didn't match. Please repeat your input."));
}
data.hash = (data.newPassword + session.data.token).md5();
}
this.value({
hash: data.hash,
salt: session.data.token
});
}
this.value({
url: evalURL(data.url),
email: evalEmail(data.email),
});
return this;
};
User.prototype.status_macro = function(param) {
// this macro is allowed just for sysadmins
if (!session.user.sysadmin) {
// This macro is allowed for privileged users only
if (!getPermission(User.PRIVILEGED)) {
return;
}
res.debug(Html.dropDown);
if (param.as === "editor") {
var options = {};
for each (var status in User.status) {
@ -312,18 +316,18 @@ User.register = function(data) {
// special characters like umlauts and spaces
var invalidChar = new RegExp("[^a-zA-Z0-9äöüß\\.\\-_ ]");
if (!data.name) {
throw new Exception("usernameMissing");
throw Error(gettext("Please enter a username."));
} else if (data.name.length > 30) {
throw new Exception("usernameTooLong");
throw Error(gettext("Sorry, the username you entered is too long. Please choose a shorter one."));
} else if (invalidChar.exec(data.name)) {
throw new Exception("usernameNoSpecialChars");
throw Error(gettext("Please enter alphanumeric characters only in the username field."));
} else if (this[data.name] || this[data.name + "_action"]) {
throw new Exception("usernameExisting");
throw Error(gettext("Sorry, the user name you entered already exists. Please enter a different one."));
}
// check if email-address is valid
if (!data.email) {
throw new Exception("emailMissing");
throw new Error(gettext("Please enter your e-mail address."));
}
evalEmail(data.email);
@ -331,26 +335,51 @@ User.register = function(data) {
if (!data.hash) {
// Check if passwords match
if (!data.password || !data.passwordConfirm) {
throw new Exception("passwordTwice");
throw new Error(gettext("Could not verify your password. Please repeat your input."))
} else if (data.password !== data.passwordConfirm) {
throw new Exception("passwordNoMatch");
throw new Error(gettext("Unfortunately, your passwords didn't match. Please repeat your input."));
}
data.hash = (data.password + session.data.token).md5();
}
if (User.getByName(data.name)) {
throw new Exception("memberExisting");
throw new Error(gettext("Sorry, the user name you entered already exists. Please enter a different one."));
}
var user = new User(data);
// grant trust and sysadmin-rights if there's no sysadmin 'til now
if (root.manage.sysadmins.size() == 0) {
user.sysadmin = user.trusted = 1;
} else {
user.sysadmin = user.trusted = 0;
if (root.manage.sysadmins.size() < 1) {
user.value("status", User.PRIVILEGED);
}
root.users.add(user);
var welcomeWhere = path.site ? path.site.title : root.getTitle();
return new Message("welcome", [welcomeWhere, user.name], user);
session.login(user);
return user;
};
User.login = function(data) {
var user = User.getByName(data.name);
if (!user) {
throw Error(gettext("Unfortunately, your login failed. Maybe a typo?"));
}
var digest = data.digest;
// Calculate digest for JavaScript-disabled browsers
if (!digest) {
digest = ((data.password + user.value("salt")).md5() +
session.data.token).md5();
}
// Check if login is correct
if (digest !== user.getDigest(session.data.token)) {
throw Error("Unfortunately, your login failed. Maybe a typo?");
}
if (data.remember) {
// Set long running cookies for automatic login
res.setCookie("avUsr", user.value("name"), 365);
var ip = req.data.http_remotehost.clip(getProperty("cookieLevel", "4"),
"", "\\.");
res.setCookie("avPw", (user.value("hash") + ip).md5(), 365);
}
user.touch();
session.login(user);
return user;
};
User.status = ["default", "blocked", "trusted", "privileged"];

View file

@ -23,7 +23,7 @@
##
_db = antville
_table = AV_USER
_table = user
_id = id
_name = name
_parent = root.users

View file

@ -37,7 +37,7 @@ poll.foreign = POLL_ID
user = object(User)
user.local = VOTE_F_USER
user.foreign = USER_ID
user.foreign = id
choice = object(Choice)
choice.local = VOTE_F_CHOICE