diff --git a/Builder/IISMainHandler/build.txt b/Builder/IISMainHandler/build.txt index 721527c..0735b80 100644 --- a/Builder/IISMainHandler/build.txt +++ b/Builder/IISMainHandler/build.txt @@ -1 +1 @@ -896 \ No newline at end of file +904 \ No newline at end of file diff --git a/Common/Common.csproj b/Common/Common.csproj index 562a994..2d5a531 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -87,6 +87,7 @@ + diff --git a/Common/actions/ChangeSet.cs b/Common/actions/ChangeSet.cs index 10407ed..6430d20 100644 --- a/Common/actions/ChangeSet.cs +++ b/Common/actions/ChangeSet.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, } ); diff --git a/Common/dataobjects/Account.cs b/Common/dataobjects/Account.cs index 97ac04b..10ddaa9 100644 --- a/Common/dataobjects/Account.cs +++ b/Common/dataobjects/Account.cs @@ -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 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() { { 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 getNewAccountChanges(string _name, string password) { + string name = _name.Trim(); + checkNewName(name); + checkNewPassword(password); + var userInsert = new InsertChange( + User.TableSpec.instance, + new Dictionary { + { 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 { + { 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 { + { AccountIndicator.TableSpec.FIELD_ACCOUNTID, new ReferenceFieldValue(accountInsert) }, + { AccountIndicator.TableSpec.FIELD_PRIVATEMESSAGES, new ScalarFieldValue("0") }, + { AccountIndicator.TableSpec.FIELD_UNREADPRIVATEMESSAGES, new ScalarFieldValue("0") }, + } + ); + return new KeyValuePair( + accountInsert, + new[] { + userInsert, + accountInsert, + indicatorInsert, + } + ); + } + + public void migrate(string newPassword) { + checkNewPassword(newPassword); ChangeSetUtil.ApplyChanges(new AbstractChange[] { new UpdateChange( TableSpec.instance, new Dictionary() { { 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; } diff --git a/Common/dataobjects/Invite.cs b/Common/dataobjects/Invite.cs new file mode 100644 index 0000000..ff1b59a --- /dev/null +++ b/Common/dataobjects/Invite.cs @@ -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 { + + 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 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(rawChanges.Value); + changes.Add( + new UpdateChange( + TableSpec.instance, + new Dictionary { + { TableSpec.FIELD_ISUSED, new ScalarFieldValue("1") }, + { TableSpec.FIELD_GUESTID, new ReferenceFieldValue(accountInsert) }, + }, + this.id + ) + ); + ChangeSetUtil.ApplyChanges(changes.ToArray()); + return Account.LoadById(accountInsert.getId().Value); + } + } + + } +} diff --git a/IISMainHandler/HandlersFactory.cs b/IISMainHandler/HandlersFactory.cs index e0904a1..13e0d31 100644 --- a/IISMainHandler/HandlersFactory.cs +++ b/IISMainHandler/HandlersFactory.cs @@ -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": diff --git a/IISMainHandler/IISMainHandler.csproj b/IISMainHandler/IISMainHandler.csproj index 5bc0c47..bdd15a9 100644 --- a/IISMainHandler/IISMainHandler.csproj +++ b/IISMainHandler/IISMainHandler.csproj @@ -60,6 +60,7 @@ + @@ -70,6 +71,7 @@ + @@ -96,6 +98,7 @@ + diff --git a/IISMainHandler/handlers/request/AbstractNewAccountHandler.cs b/IISMainHandler/handlers/request/AbstractNewAccountHandler.cs new file mode 100644 index 0000000..4c916cc --- /dev/null +++ b/IISMainHandler/handlers/request/AbstractNewAccountHandler.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]; + } + + } +} diff --git a/IISMainHandler/handlers/request/MigrateAccountHandler.cs b/IISMainHandler/handlers/request/MigrateAccountHandler.cs index 0e5642c..d799682 100644 --- a/IISMainHandler/handlers/request/MigrateAccountHandler.cs +++ b/IISMainHandler/handlers/request/MigrateAccountHandler.cs @@ -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; } } diff --git a/IISMainHandler/handlers/request/RegisterByInviteHandler.cs b/IISMainHandler/handlers/request/RegisterByInviteHandler.cs new file mode 100644 index 0000000..086afe6 --- /dev/null +++ b/IISMainHandler/handlers/request/RegisterByInviteHandler.cs @@ -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"]); + } + + } +} diff --git a/IISMainHandler/handlers/response/RegisterByInviteHandler.cs b/IISMainHandler/handlers/response/RegisterByInviteHandler.cs new file mode 100644 index 0000000..cde3527 --- /dev/null +++ b/IISMainHandler/handlers/response/RegisterByInviteHandler.cs @@ -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 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), + }; + } + + } +} diff --git a/templates/Full/RegisterByInvite.xslt b/templates/Full/RegisterByInvite.xslt new file mode 100644 index 0000000..1a8c75b --- /dev/null +++ b/templates/Full/RegisterByInvite.xslt @@ -0,0 +1,55 @@ + + + + Ðåãèñòðàöèÿ + + + + + +
+ + + + + + + +
+ Ðåãèñòðàöèÿ ïî èíâàéòó +
+
+ + + + + + + Èìÿ ïîëüçîâàòåëÿ
+
+ Íîâûé ïàðîëü
+
+ Ïîâòîðèòå ïàðîëü
+
+
+ + + îïèñàííàÿ ïî ýòîé ññûëêå êîíñòèòóöèÿ + . + (îáÿçàòåëüíî) +
+ + + (îáÿçàòåëüíî) +
+ + + (îáÿçàòåëüíî) +
+ +
+
+
+
+ +
\ No newline at end of file diff --git a/templates/Full/result/MigrateAccount.xslt b/templates/Full/result/NewAccount.xslt similarity index 81% rename from templates/Full/result/MigrateAccount.xslt rename to templates/Full/result/NewAccount.xslt index b60e2c9..38be2ad 100644 --- a/templates/Full/result/MigrateAccount.xslt +++ b/templates/Full/result/NewAccount.xslt @@ -1,7 +1,7 @@ - Ó÷¸òíàÿ çàïèñü ñìèãðèðîâàíà + Ó÷¸òíàÿ çàïèñü ñîçäàíà @@ -9,7 +9,7 @@
- Ìèãðàöèÿ ïîëüçîâàòåëÿ + Ñîçäàíèå ó÷¸òíîé çàïèñè
@@ -20,7 +20,7 @@
-

Ìèãðàöèÿ áûëà óñïåøíî çàâåðøåíà, òåïåðü âû ìîæåòå âîéòè â ôîðóì, èñïîëüçóÿ ñâîé ëîãèí è íîâûé ïàðîëü

+

Ó÷¸òíàÿ çàïèñü áûëà óñïåøíî ñîçäàíà, òåïåðü âû ìîæåòå âîéòè â ôîðóì, èñïîëüçóÿ ñâîé ëîãèí è íîâûé ïàðîëü

[←]