Creation of new users implemented; registration by invites implemented

main
Inga 🏳‍🌈 15 years ago
parent 918d1092fc
commit b74fa316b9
  1. 2
      Builder/IISMainHandler/build.txt
  2. 1
      Common/Common.csproj
  3. 1
      Common/actions/ChangeSet.cs
  4. 91
      Common/dataobjects/Account.cs
  5. 110
      Common/dataobjects/Invite.cs
  6. 4
      IISMainHandler/HandlersFactory.cs
  7. 3
      IISMainHandler/IISMainHandler.csproj
  8. 49
      IISMainHandler/handlers/request/AbstractNewAccountHandler.cs
  9. 31
      IISMainHandler/handlers/request/MigrateAccountHandler.cs
  10. 24
      IISMainHandler/handlers/request/RegisterByInviteHandler.cs
  11. 30
      IISMainHandler/handlers/response/RegisterByInviteHandler.cs
  12. 55
      templates/Full/RegisterByInvite.xslt
  13. 6
      templates/Full/result/NewAccount.xslt

@ -87,6 +87,7 @@
<Compile Include="dataobjects\AccountIndicator.cs" />
<Compile Include="dataobjects\Board.cs" />
<Compile Include="dataobjects\Category.cs" />
<Compile Include="dataobjects\Invite.cs" />
<Compile Include="dataobjects\IUserSettings.cs" />
<Compile Include="dataobjects\AnonymousUserSettings.cs" />
<Compile Include="dataobjects\PMConversation.cs" />

@ -33,6 +33,7 @@ namespace FLocal.Common.actions {
dataobjects.Board.ReadMarkerTableSpec.TABLE,
dataobjects.Poll.TableSpec.TABLE,
dataobjects.Poll.Vote.TableSpec.TABLE,
dataobjects.Invite.TableSpec.TABLE,
dataobjects.Session.TableSpec.TABLE,
}
);

@ -55,10 +55,19 @@ namespace FLocal.Common.dataobjects {
}
}
private string _name;
public string name {
get {
this.LoadIfNotLoaded();
return this._name;
}
}
protected override void doFromHash(Dictionary<string, string> data) {
this._userId = int.Parse(data[TableSpec.FIELD_USERID]);
this._passwordHash = data[TableSpec.FIELD_PASSWORDHASH];
this._needsMigration = Util.string2bool(data[TableSpec.FIELD_NEEDSMIGRATION]);
this._name = data[TableSpec.FIELD_NAME];
}
public XElement exportToXml(UserContext context) {
@ -103,23 +112,31 @@ namespace FLocal.Common.dataobjects {
return Account.LoadById(userid2id[user.id]);
}
private string hashPassword(string password) {
private string hashPasswordLegacy(string password) {
return Util.md5(Util.md5(password) + " " + Util.md5(Config.instance.SaltMigration) + " " + Util.md5(this.id.ToString()));
}
private static string hashPassword(string password, string name) {
return Util.md5(Util.md5(password) + " " + Util.md5(Config.instance.SaltMigration) + " " + Util.md5(name));
}
private string hashPassword(string password) {
return hashPassword(password, name);
}
public bool checkPassword(string password) {
return this.hashPassword(password) == this.passwordHash;
return (this.hashPassword(password) == this.passwordHash) || (this.hashPasswordLegacy(password) == this.passwordHash);
}
public void updatePassword(string newPassword) {
if(newPassword.Length < 5) throw new FLocalException("Password is too short");
checkNewPassword(newPassword);
ChangeSetUtil.ApplyChanges(new AbstractChange[] {
new UpdateChange(
TableSpec.instance,
new Dictionary<string, AbstractFieldValue>() {
{
TableSpec.FIELD_PASSWORDHASH,
new ScalarFieldValue(this.hashPassword(newPassword))
new ScalarFieldValue(this.hashPasswordLegacy(newPassword))
},
},
this.id
@ -127,15 +144,75 @@ namespace FLocal.Common.dataobjects {
});
}
public void migrate(string newPassword) {
public static void checkNewPassword(string newPassword) {
if(newPassword.Length < 5) throw new FLocalException("Password is too short");
}
public static void checkNewName(string newName) {
if(newName.Length < 2) throw new FLocalException("Name is too short");
if(newName.Length > 16) throw new FLocalException("Name is too long");
try {
Account.LoadByName(newName);
throw new FLocalException("Name is already used");
} catch(NotFoundInDBException) {
}
}
public static KeyValuePair<AbstractChange, AbstractChange[]> getNewAccountChanges(string _name, string password) {
string name = _name.Trim();
checkNewName(name);
checkNewPassword(password);
var userInsert = new InsertChange(
User.TableSpec.instance,
new Dictionary<string, AbstractFieldValue> {
{ User.TableSpec.FIELD_AVATARID, new ScalarFieldValue(null) },
{ User.TableSpec.FIELD_BIOGRAPHY, new ScalarFieldValue("") },
{ User.TableSpec.FIELD_LOCATION, new ScalarFieldValue("") },
{ User.TableSpec.FIELD_NAME, new ScalarFieldValue(name) },
{ User.TableSpec.FIELD_REGDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) },
{ User.TableSpec.FIELD_SHOWPOSTSTOUSERS, new ScalarFieldValue(User.ENUM_SHOWPOSTSTOUSERS_ALL) },
{ User.TableSpec.FIELD_SIGNATURE, new ScalarFieldValue("") },
{ User.TableSpec.FIELD_TITLE, new ScalarFieldValue("novice") },
{ User.TableSpec.FIELD_TOTALPOSTS, new ScalarFieldValue("0") },
{ User.TableSpec.FIELD_USERGROUPID, new ScalarFieldValue("1") },
}
);
var accountInsert = new InsertChange(
Account.TableSpec.instance,
new Dictionary<string, AbstractFieldValue> {
{ Account.TableSpec.FIELD_NAME, new ScalarFieldValue(name.ToLower()) },
{ Account.TableSpec.FIELD_NEEDSMIGRATION, new ScalarFieldValue("0") },
{ Account.TableSpec.FIELD_PASSWORDHASH, new ScalarFieldValue(hashPassword(password, name.ToLower())) },
{ Account.TableSpec.FIELD_USERID, new ReferenceFieldValue(userInsert) },
}
);
var indicatorInsert = new InsertChange(
AccountIndicator.TableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ AccountIndicator.TableSpec.FIELD_ACCOUNTID, new ReferenceFieldValue(accountInsert) },
{ AccountIndicator.TableSpec.FIELD_PRIVATEMESSAGES, new ScalarFieldValue("0") },
{ AccountIndicator.TableSpec.FIELD_UNREADPRIVATEMESSAGES, new ScalarFieldValue("0") },
}
);
return new KeyValuePair<AbstractChange,AbstractChange[]>(
accountInsert,
new[] {
userInsert,
accountInsert,
indicatorInsert,
}
);
}
public void migrate(string newPassword) {
checkNewPassword(newPassword);
ChangeSetUtil.ApplyChanges(new AbstractChange[] {
new UpdateChange(
TableSpec.instance,
new Dictionary<string, AbstractFieldValue>() {
{
TableSpec.FIELD_PASSWORDHASH,
new ScalarFieldValue(this.hashPassword(newPassword))
new ScalarFieldValue(this.hashPasswordLegacy(newPassword))
},
{
TableSpec.FIELD_NEEDSMIGRATION,
@ -156,7 +233,7 @@ namespace FLocal.Common.dataobjects {
public static Account tryAuthorize(string name, string password) {
Account account = LoadByName(name);
if(account.passwordHash != account.hashPassword(password)) throw new FLocalException("Wrong password (" + account.id + ":" + account.hashPassword(password) + ")");
if(!account.checkPassword(password)) throw new FLocalException("Wrong password (" + account.id + ":" + account.hashPassword(password) + ")");
return account;
}

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using FLocal.Core;
using FLocal.Core.DB;
using FLocal.Core.DB.conditions;
using FLocal.Common.actions;
namespace FLocal.Common.dataobjects {
public class Invite : SqlObject<Invite> {
public class TableSpec : ISqlObjectTableSpec {
public const string TABLE = "Invites";
public const string FIELD_ID = "Id";
public const string FIELD_CODE = "Code";
public const string FIELD_ISUSED = "IsUsed";
public const string FIELD_OWNERID = "OwnerId";
public const string FIELD_GUESTID = "GuestId";
public static readonly TableSpec instance = new TableSpec();
public string name { get { return TABLE; } }
public string idName { get { return FIELD_ID; } }
public void refreshSqlObject(int id) { Refresh(id); }
}
protected override ISqlObjectTableSpec table { get { return TableSpec.instance; } }
private string _code;
public string code {
get {
this.LoadIfNotLoaded();
return this._code;
}
}
private bool _isUsed;
public bool isUsed {
get {
this.LoadIfNotLoaded();
return this._isUsed;
}
}
private int _ownerId;
public int ownerId {
get {
this.LoadIfNotLoaded();
return this._ownerId;
}
}
public Account owner {
get {
return Account.LoadById(this.ownerId);
}
}
private int? _invitedId;
public int? invitedId {
get {
this.LoadIfNotLoaded();
return this._invitedId;
}
}
public Account invited {
get {
return Account.LoadById(this.invitedId.Value);
}
}
protected override void doFromHash(Dictionary<string, string> data) {
this._code = data[TableSpec.FIELD_CODE];
this._isUsed = Util.string2bool(data[TableSpec.FIELD_ISUSED]);
this._ownerId = int.Parse(data[TableSpec.FIELD_OWNERID]);
this._invitedId = Util.ParseInt(data[TableSpec.FIELD_GUESTID]);
}
public XElement exportToXml(UserContext context) {
return new XElement("invite",
new XElement("id", this.id),
new XElement("code", this.code),
new XElement("isUsed", this.isUsed.ToPlainString())
);
}
private object createAccount_locker = new object();
public Account createAccount(string code, string name, string password) {
lock(this.createAccount_locker) {
if(this.isUsed) throw new FLocalException("Invite is already used");
if(this.code != code) throw new FLocalException("Wrong code");
var rawChanges = Account.getNewAccountChanges(name, password);
var accountInsert = rawChanges.Key;
var changes = new List<AbstractChange>(rawChanges.Value);
changes.Add(
new UpdateChange(
TableSpec.instance,
new Dictionary<string,AbstractFieldValue> {
{ TableSpec.FIELD_ISUSED, new ScalarFieldValue("1") },
{ TableSpec.FIELD_GUESTID, new ReferenceFieldValue(accountInsert) },
},
this.id
)
);
ChangeSetUtil.ApplyChanges(changes.ToArray());
return Account.LoadById(accountInsert.getId().Value);
}
}
}
}

@ -78,6 +78,8 @@ namespace FLocal.IISHandler {
return new handlers.response.LoginHandler();
case "migrateaccount":
return new handlers.response.MigrateAccountHandler();
case "registerbyinvite":
return new handlers.response.RegisterByInviteHandler();
case "users":
if(context.requestParts.Length < 2) {
return new handlers.response.UserListHandler();
@ -165,6 +167,8 @@ namespace FLocal.IISHandler {
return new handlers.request.LogoutHandler();
case "migrateaccount":
return new handlers.request.MigrateAccountHandler();
case "registerbyinvite":
return new handlers.request.RegisterByInviteHandler();
case "edit":
return new handlers.request.EditHandler();
case "reply":

@ -60,6 +60,7 @@
<Compile Include="handlers\BoardsHandler.cs" />
<Compile Include="handlers\DebugHandler.cs" />
<Compile Include="handlers\PostHandler.cs" />
<Compile Include="handlers\request\AbstractNewAccountHandler.cs" />
<Compile Include="handlers\request\AbstractNewMessageHandler.cs" />
<Compile Include="handlers\request\AbstractPostHandler.cs" />
<Compile Include="handlers\request\CreatePollHandler.cs" />
@ -70,6 +71,7 @@
<Compile Include="handlers\request\maintenance\CleanCacheHandler.cs" />
<Compile Include="handlers\request\MarkThreadAsReadHandler.cs" />
<Compile Include="handlers\request\MigrateAccountHandler.cs" />
<Compile Include="handlers\request\RegisterByInviteHandler.cs" />
<Compile Include="handlers\request\ReturnPostHandler.cs" />
<Compile Include="handlers\request\ReplyHandler.cs" />
<Compile Include="handlers\request\SendPMHandler.cs" />
@ -96,6 +98,7 @@
<Compile Include="handlers\response\PollHandler.cs" />
<Compile Include="handlers\response\QuickLinkHandler.cs" />
<Compile Include="handlers\response\RedirectGetHandler.cs" />
<Compile Include="handlers\response\RegisterByInviteHandler.cs" />
<Compile Include="handlers\response\ReplyHandler.cs" />
<Compile Include="handlers\response\RobotsHandler.cs" />
<Compile Include="handlers\response\SettingsHandler.cs" />

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using FLocal.Common.dataobjects;
using FLocal.Importer;
using System.Text.RegularExpressions;
using FLocal.Core;
using FLocal.Common;
using FLocal.Common.actions;
namespace FLocal.IISHandler.handlers.request {
abstract class AbstractNewAccountHandler : AbstractPostHandler {
protected override string templateName {
get {
return "result/NewAccount.xslt";
}
}
protected override bool shouldBeLoggedIn {
get { return false; }
}
sealed protected override bool shouldBeGuest {
get { return true; }
}
abstract protected Account DoCreateAccount(WebContext context);
sealed protected override XElement[] Do(WebContext context) {
if(context.httprequest.Form["constitution"] != "constitution") {
throw new FLocalException("constitution not accepted");
}
if(context.httprequest.Form["showPostsToAll"] != "showPostsToAll") {
throw new FLocalException("publicity not accepted");
}
if(context.httprequest.Form["law"] != "law") {
throw new FLocalException("laws not accepted");
}
this.DoCreateAccount(context);
return new XElement[0];
}
}
}

@ -11,34 +11,9 @@ using FLocal.Common;
using FLocal.Common.actions;
namespace FLocal.IISHandler.handlers.request {
class MigrateAccountHandler : AbstractPostHandler {
protected override string templateName {
get {
return "result/MigrateAccount.xslt";
}
}
protected override bool shouldBeLoggedIn {
get { return false; }
}
protected override bool shouldBeGuest {
get { return true; }
}
protected override XElement[] Do(WebContext context) {
if(context.httprequest.Form["constitution"] != "constitution") {
throw new FLocalException("constitution not accepted");
}
if(context.httprequest.Form["showPostsToAll"] != "showPostsToAll") {
throw new FLocalException("publicity not accepted");
}
if(context.httprequest.Form["law"] != "law") {
throw new FLocalException("laws not accepted");
}
class MigrateAccountHandler : AbstractNewAccountHandler {
protected override Account DoCreateAccount(WebContext context) {
Account account = Account.LoadById(int.Parse(context.httprequest.Form["accountId"]));
if(!account.needsMigration) throw new FLocalException("Already migrated");
string userInfo = ShallerGateway.getUserInfoAsString(account.user.name);
@ -49,7 +24,7 @@ namespace FLocal.IISHandler.handlers.request {
if(check != context.httprequest["check"]) throw new FLocalException("Wrong key (fhn:" + match.Groups[1].Value + ")");
if(context.httprequest.Form["password"] != context.httprequest.Form["password2"]) throw new FLocalException("Passwords mismatch");
account.migrate(context.httprequest.Form["password2"]);
return new XElement[0];
return account;
}
}

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using FLocal.Common.dataobjects;
using FLocal.Importer;
using System.Text.RegularExpressions;
using FLocal.Core;
using FLocal.Common;
using FLocal.Common.actions;
namespace FLocal.IISHandler.handlers.request {
class RegisterByInviteHandler : AbstractNewAccountHandler {
protected override Account DoCreateAccount(WebContext context) {
Invite invite = Invite.LoadById(int.Parse(context.httprequest.Form["inviteId"]));
if(invite.isUsed) throw new FLocalException("Invite is already used");
if(context.httprequest.Form["password"] != context.httprequest.Form["password2"]) throw new FLocalException("Passwords mismatch");
return invite.createAccount(context.httprequest.Form["code"], context.httprequest.Form["login"], context.httprequest.Form["password"]);
}
}
}

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using FLocal.Core;
using FLocal.Common.dataobjects;
namespace FLocal.IISHandler.handlers.response {
class RegisterByInviteHandler : AbstractGetHandler {
protected override string templateName {
get {
return "RegisterByInvite.xslt";
}
}
protected override IEnumerable<XElement> getSpecificData(WebContext context) {
int inviteId = int.Parse(context.requestParts[1]);
string code = context.requestParts[2];
Invite invite = Invite.LoadById(inviteId);
if(invite.isUsed) throw new FLocalException("Invite is already used");
if(invite.code != code) throw new FLocalException("Code mismatch");
return new XElement[] {
invite.exportToXml(context),
};
}
}
}

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="Windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:import href="elems\Main.xslt"/>
<xsl:template name="specificTitle">Ðåãèñòðàöèÿ</xsl:template>
<xsl:template name="specific">
<table width="95%" align="center" cellpadding="1" cellspacing="1" class="tablesurround">
<tr>
<td>
<table cellpadding="3" cellspacing="1" width="100%" class="tableborders">
<tr>
<td class="tdheader">
<xsl:text>Ðåãèñòðàöèÿ ïî èíâàéòó</xsl:text>
</td>
</tr>
<tr>
<td class="lighttable">
<form method="post" action="/do/RegisterByInvite/">
<input type="hidden" name="inviteId">
<xsl:attribute name="value"><xsl:value-of select="invite/id"/></xsl:attribute>
</input>
<input type="hidden" name="code">
<xsl:attribute name="value"><xsl:value-of select="invite/code"/></xsl:attribute>
</input>
<xsl:text>Èìÿ ïîëüçîâàòåëÿ</xsl:text><br/>
<input type="text" name="login" class="formboxes" maxlength="16"/><br/>
<xsl:text>Íîâûé ïàðîëü</xsl:text><br />
<input type="password" name="password" class="formboxes" /><br/>
<xsl:text>Ïîâòîðèòå ïàðîëü</xsl:text><br/>
<input type="password" name="password2" class="formboxes" /><br/>
<br/>
<input type="checkbox" name="constitution" value="constitution" id="constitution"/>
<label for="constitution"> Äà, ÿ ñîãëàñåí/ñîãëàñíà/ñîãëàñíî ñ òåì, ÷òî íà ýòîì ôîðóìå äåéñòâóåò </label>
<a href="/q/constitution/" style="text-decoration:underline">îïèñàííàÿ ïî ýòîé ññûëêå êîíñòèòóöèÿ</a>
<xsl:text>.</xsl:text>
<i> (îáÿçàòåëüíî)</i>
<br/>
<input type="checkbox" name="showPostsToAll" value="showPostsToAll" id="showPostsToAll"/>
<label for="showPostsToAll"> Äà, ÿ ñîãëàñåí/ñîãëàñíà/ñîãëàñíî ñ òåì, ÷òîáû ìîè ñîîáùåíèÿ, ðàçìåù¸ííûå íà ýòîì ôîðóìå, áûëè äîñòóïíû ïóáëè÷íî.</label>
<i> (îáÿçàòåëüíî)</i>
<br/>
<input type="checkbox" name="law" value="law" id="law"/>
<label for="law"> Äà, ÿ îáÿçóþñü ñîáëþäàòü çàêîíû Ðîññèéñêîé Ôåäåðàöèè è Ôåäåðàòèâíîé Ðåñïóáëèêè Ãåðìàíèÿ â îáùåíèè íà ýòîì ôîðóìå.</label>
<i> (îáÿçàòåëüíî)</i>
<br/>
<input type="submit" name="buttlogin" value="Çàðåãèñòðèðîâàòüñÿ!" class="buttons" />
</form>
</td>
</tr>
</table>
</td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="Windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:import href="..\elems\Main.xslt"/>
<xsl:template name="specificTitle">Учётная запись смигрирована</xsl:template>
<xsl:template name="specificTitle">Учётная запись создана</xsl:template>
<xsl:template name="specific">
<table width="95%" align="center" cellpadding="1" cellspacing="1" class="tablesurround">
<tr>
@ -9,7 +9,7 @@
<table cellpadding="3" cellspacing="1" width="100%" class="tableborders">
<tr>
<td class="tdheader">
<xsl:text>Миграция пользователя</xsl:text>
<xsl:text>Создание учётной записи</xsl:text>
</td>
</tr>
</table>
@ -20,7 +20,7 @@
<table cellpadding="3" cellspacing="1" width="100%" class="tableborders">
<tr>
<td class="lighttable">
<p>Миграция была успешно завершена, теперь вы можете войти в форум, используя свой логин и новый пароль</p>
<p>Учётная запись была успешно создана, теперь вы можете войти в форум, используя свой логин и новый пароль</p>
<p align="center">
<xsl:text>[&#8592;] </xsl:text>
<a id="actionLink_left">
Loading…
Cancel
Save