aboutsummaryrefslogtreecommitdiff
path: root/Timeline
diff options
context:
space:
mode:
Diffstat (limited to 'Timeline')
-rw-r--r--Timeline/Auth/MyAuthenticationHandler.cs4
-rw-r--r--Timeline/Controllers/UserController.cs4
-rw-r--r--Timeline/Models/User.cs1
-rw-r--r--Timeline/Models/Validation/NicknameValidator.cs26
-rw-r--r--Timeline/Resources/Messages.zh.resx189
-rw-r--r--Timeline/Resources/Models/Http/Common.zh.resx132
-rw-r--r--Timeline/Resources/Models/Validation/NicknameValidator.Designer.cs (renamed from Timeline/Resources/Models/Validation/PasswordValidator.Designer.cs)12
-rw-r--r--Timeline/Resources/Models/Validation/NicknameValidator.resx (renamed from Timeline/Resources/Services/UserManager.resx)4
-rw-r--r--Timeline/Resources/Models/Validation/PasswordValidator.resx123
-rw-r--r--Timeline/Resources/Models/Validation/PasswordValidator.zh.resx123
-rw-r--r--Timeline/Resources/Models/Validation/UsernameValidator.zh.resx129
-rw-r--r--Timeline/Resources/Models/Validation/Validator.zh.resx129
-rw-r--r--Timeline/Resources/Services/Exception.Designer.cs18
-rw-r--r--Timeline/Resources/Services/Exception.resx6
-rw-r--r--Timeline/Resources/Services/UserManager.Designer.cs72
-rw-r--r--Timeline/Resources/Services/UserService.Designer.cs36
-rw-r--r--Timeline/Resources/Services/UserService.resx18
-rw-r--r--Timeline/Services/ConfictException.cs21
-rw-r--r--Timeline/Services/UserService.cs263
-rw-r--r--Timeline/Services/UserTokenException.cs3
-rw-r--r--Timeline/Services/UserTokenManager.cs4
-rw-r--r--Timeline/Services/UsernameConfictException.cs25
-rw-r--r--Timeline/Startup.cs19
-rw-r--r--Timeline/Timeline.csproj17
24 files changed, 265 insertions, 1113 deletions
diff --git a/Timeline/Auth/MyAuthenticationHandler.cs b/Timeline/Auth/MyAuthenticationHandler.cs
index 5bae5117..e6b26c4b 100644
--- a/Timeline/Auth/MyAuthenticationHandler.cs
+++ b/Timeline/Auth/MyAuthenticationHandler.cs
@@ -82,9 +82,9 @@ namespace Timeline.Auth
var userInfo = await _userTokenManager.VerifyToken(token);
var identity = new ClaimsIdentity(AuthenticationConstants.Scheme);
- identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userInfo.Id.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64));
+ identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userInfo.Id!.Value.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64));
identity.AddClaim(new Claim(identity.NameClaimType, userInfo.Username, ClaimValueTypes.String));
- identity.AddClaims(UserRoleConvert.ToArray(userInfo.Administrator).Select(role => new Claim(identity.RoleClaimType, role, ClaimValueTypes.String)));
+ identity.AddClaims(UserRoleConvert.ToArray(userInfo.Administrator!.Value).Select(role => new Claim(identity.RoleClaimType, role, ClaimValueTypes.String)));
var principal = new ClaimsPrincipal();
principal.AddIdentity(identity);
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs
index 5f1b7bd7..3305952a 100644
--- a/Timeline/Controllers/UserController.cs
+++ b/Timeline/Controllers/UserController.cs
@@ -29,7 +29,7 @@ namespace Timeline.Controllers
[HttpGet("users"), AdminAuthorize]
public async Task<ActionResult<User[]>> List()
{
- return Ok(await _userService.ListUsers());
+ return Ok(await _userService.GetUsers());
}
[HttpGet("users/{username}"), AdminAuthorize]
@@ -105,7 +105,7 @@ namespace Timeline.Controllers
("Old Username", request.OldUsername), ("New Username", request.NewUsername)));
return BadRequest(ErrorResponse.UserCommon.NotExist());
}
- catch (UsernameConfictException e)
+ catch (ConfictException e)
{
_logger.LogInformation(e, Log.Format(LogChangeUsernameConflict,
("Old Username", request.OldUsername), ("New Username", request.NewUsername)));
diff --git a/Timeline/Models/User.cs b/Timeline/Models/User.cs
index 05395022..2cead892 100644
--- a/Timeline/Models/User.cs
+++ b/Timeline/Models/User.cs
@@ -12,6 +12,7 @@ namespace Timeline.Models
#region secret
+ public long? Id { get; set; }
public string? Password { get; set; }
public long? Version { get; set; }
#endregion secret
diff --git a/Timeline/Models/Validation/NicknameValidator.cs b/Timeline/Models/Validation/NicknameValidator.cs
new file mode 100644
index 00000000..f6626a2a
--- /dev/null
+++ b/Timeline/Models/Validation/NicknameValidator.cs
@@ -0,0 +1,26 @@
+using System;
+using static Timeline.Resources.Models.Validation.NicknameValidator;
+
+namespace Timeline.Models.Validation
+{
+ public class NicknameValidator : Validator<string>
+ {
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Already checked in base.")]
+ protected override (bool, string) DoValidate(string value)
+ {
+ if (value.Length > 10)
+ return (false, MessageTooLong);
+
+ return (true, GetSuccessMessage());
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
+ public class NicknameAttribute : ValidateWithAttribute
+ {
+ public NicknameAttribute() : base(typeof(NicknameValidator))
+ {
+
+ }
+ }
+}
diff --git a/Timeline/Resources/Messages.zh.resx b/Timeline/Resources/Messages.zh.resx
deleted file mode 100644
index 6e52befd..00000000
--- a/Timeline/Resources/Messages.zh.resx
+++ /dev/null
@@ -1,189 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="Common_Content_TooBig" xml:space="preserve">
- <value>请求体太大。它不能超过{0}.</value>
- </data>
- <data name="Common_Content_UnmatchedLength_Bigger" xml:space="preserve">
- <value>实际的请求体长度比头中指示的大。</value>
- </data>
- <data name="Common_Content_UnmatchedLength_Smaller" xml:space="preserve">
- <value>实际的请求体长度比头中指示的小。</value>
- </data>
- <data name="Common_Forbid" xml:space="preserve">
- <value>你没有权限做此操作。</value>
- </data>
- <data name="Common_Header_ContentLength_Missing" xml:space="preserve">
- <value>请求头Content-Length缺失或者格式不对。</value>
- </data>
- <data name="Common_Header_ContentLength_Zero" xml:space="preserve">
- <value>请求头Content-Length不能为0。</value>
- </data>
- <data name="Common_Header_ContentType_Missing" xml:space="preserve">
- <value>请求头Content-Type缺失。</value>
- </data>
- <data name="Common_Header_IfNonMatch_BadFormat" xml:space="preserve">
- <value>请求头If-Non-Match格式不对。</value>
- </data>
- <data name="Common_InvalidModel" xml:space="preserve">
- <value>请求模型格式不对。</value>
- </data>
- <data name="TimelineController_ChangeMember_UsernameBadFormat" xml:space="preserve">
- <value>第{0}个做{1}操作的用户名格式错误。</value>
- </data>
- <data name="TimelineController_ChangeMember_UserNotExist" xml:space="preserve">
- <value>第{0}个做{1}操作的用户不存在。</value>
- </data>
- <data name="TimelineController_PostOperationDelete_NotExist" xml:space="preserve">
- <value>要删除的消息不存在。</value>
- </data>
- <data name="TokenController_Create_BadCredential" xml:space="preserve">
- <value>用户名或密码错误。</value>
- </data>
- <data name="TokenController_Verify_BadFormat" xml:space="preserve">
- <value>符号格式错误。这个符号可能不是这个服务器创建的。</value>
- </data>
- <data name="TokenController_Verify_OldVersion" xml:space="preserve">
- <value>符号是一个旧版本。用户可能已经更新了信息。</value>
- </data>
- <data name="TokenController_Verify_TimeExpired" xml:space="preserve">
- <value>符号过期了。</value>
- </data>
- <data name="TokenController_Verify_UserNotExist" xml:space="preserve">
- <value>用户不存在。管理员可能已经删除了这个用户。</value>
- </data>
- <data name="UserAvatar_BadFormat_BadSize" xml:space="preserve">
- <value>图片不是正方形。</value>
- </data>
- <data name="UserAvatar_BadFormat_CantDecode" xml:space="preserve">
- <value>解码图片失败。</value>
- </data>
- <data name="UserAvatar_BadFormat_UnmatchedFormat" xml:space="preserve">
- <value>图片格式与请求头中指示的不一样。</value>
- </data>
- <data name="UserCommon_NotExist" xml:space="preserve">
- <value>要操作的用户不存在。</value>
- </data>
- <data name="UserController_ChangePassword_BadOldPassword" xml:space="preserve">
- <value>旧密码错误。</value>
- </data>
- <data name="UserController_ChangeUsername_Conflict" xml:space="preserve">
- <value>新用户名已经存在。</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Models/Http/Common.zh.resx b/Timeline/Resources/Models/Http/Common.zh.resx
deleted file mode 100644
index de74ac3b..00000000
--- a/Timeline/Resources/Models/Http/Common.zh.resx
+++ /dev/null
@@ -1,132 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="MessageDeleteDelete" xml:space="preserve">
- <value>删除了一个项目。</value>
- </data>
- <data name="MessageDeleteNotExist" xml:space="preserve">
- <value>要删除的项目不存在,什么都没有修改。</value>
- </data>
- <data name="MessagePutCreate" xml:space="preserve">
- <value>创建了一个新项目。</value>
- </data>
- <data name="MessagePutModify" xml:space="preserve">
- <value>修改了一个已存在的项目。</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Models/Validation/PasswordValidator.Designer.cs b/Timeline/Resources/Models/Validation/NicknameValidator.Designer.cs
index e7630d26..522f305a 100644
--- a/Timeline/Resources/Models/Validation/PasswordValidator.Designer.cs
+++ b/Timeline/Resources/Models/Validation/NicknameValidator.Designer.cs
@@ -22,14 +22,14 @@ namespace Timeline.Resources.Models.Validation {
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class PasswordValidator {
+ internal class NicknameValidator {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal PasswordValidator() {
+ internal NicknameValidator() {
}
/// <summary>
@@ -39,7 +39,7 @@ namespace Timeline.Resources.Models.Validation {
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Validation.PasswordValidator", typeof(PasswordValidator).Assembly);
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Validation.NicknameValidator", typeof(NicknameValidator).Assembly);
resourceMan = temp;
}
return resourceMan;
@@ -61,11 +61,11 @@ namespace Timeline.Resources.Models.Validation {
}
/// <summary>
- /// Looks up a localized string similar to Password can&apos;t be empty..
+ /// Looks up a localized string similar to Nickname is too long..
/// </summary>
- internal static string MessageEmptyString {
+ internal static string MessageTooLong {
get {
- return ResourceManager.GetString("MessageEmptyString", resourceCulture);
+ return ResourceManager.GetString("MessageTooLong", resourceCulture);
}
}
}
diff --git a/Timeline/Resources/Services/UserManager.resx b/Timeline/Resources/Models/Validation/NicknameValidator.resx
index ecb89179..b191b505 100644
--- a/Timeline/Resources/Services/UserManager.resx
+++ b/Timeline/Resources/Models/Validation/NicknameValidator.resx
@@ -117,7 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
- <data name="LogUserCreate" xml:space="preserve">
- <value>A user has been created.</value>
+ <data name="MessageTooLong" xml:space="preserve">
+ <value>Nickname is too long.</value>
</data>
</root> \ No newline at end of file
diff --git a/Timeline/Resources/Models/Validation/PasswordValidator.resx b/Timeline/Resources/Models/Validation/PasswordValidator.resx
deleted file mode 100644
index f445cc75..00000000
--- a/Timeline/Resources/Models/Validation/PasswordValidator.resx
+++ /dev/null
@@ -1,123 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="MessageEmptyString" xml:space="preserve">
- <value>Password can't be empty.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Models/Validation/PasswordValidator.zh.resx b/Timeline/Resources/Models/Validation/PasswordValidator.zh.resx
deleted file mode 100644
index 9eab7b4e..00000000
--- a/Timeline/Resources/Models/Validation/PasswordValidator.zh.resx
+++ /dev/null
@@ -1,123 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="MessageEmptyString" xml:space="preserve">
- <value>密码不能为空。</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Models/Validation/UsernameValidator.zh.resx b/Timeline/Resources/Models/Validation/UsernameValidator.zh.resx
deleted file mode 100644
index 89d519b0..00000000
--- a/Timeline/Resources/Models/Validation/UsernameValidator.zh.resx
+++ /dev/null
@@ -1,129 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="MessageEmptyString" xml:space="preserve">
- <value>空字符串是不允许的。</value>
- </data>
- <data name="MessageInvalidChar" xml:space="preserve">
- <value>无效的字符,只能使用字母、数字、下划线和连字符。</value>
- </data>
- <data name="MessageTooLong" xml:space="preserve">
- <value>太长了,不能大于26个字符。</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Models/Validation/Validator.zh.resx b/Timeline/Resources/Models/Validation/Validator.zh.resx
deleted file mode 100644
index 2f98e7e3..00000000
--- a/Timeline/Resources/Models/Validation/Validator.zh.resx
+++ /dev/null
@@ -1,129 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ValidatorMessageBadType" xml:space="preserve">
- <value>值不是类型{0}的实例。</value>
- </data>
- <data name="ValidatorMessageNull" xml:space="preserve">
- <value>值不能为null.</value>
- </data>
- <data name="ValidatorMessageSuccess" xml:space="preserve">
- <value>验证成功。</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Services/Exception.Designer.cs b/Timeline/Resources/Services/Exception.Designer.cs
index 671c4b93..cada1788 100644
--- a/Timeline/Resources/Services/Exception.Designer.cs
+++ b/Timeline/Resources/Services/Exception.Designer.cs
@@ -115,6 +115,15 @@ namespace Timeline.Resources.Services {
}
/// <summary>
+ /// Looks up a localized string similar to A present resource conflicts with the given resource..
+ /// </summary>
+ internal static string ConfictException {
+ get {
+ return ResourceManager.GetString("ConfictException", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The hashes password is of bad format. It might not be created by server..
/// </summary>
internal static string HashedPasswordBadFromatException {
@@ -313,15 +322,6 @@ namespace Timeline.Resources.Services {
}
/// <summary>
- /// Looks up a localized string similar to The username already exists..
- /// </summary>
- internal static string UsernameConfictException {
- get {
- return ResourceManager.GetString("UsernameConfictException", resourceCulture);
- }
- }
-
- /// <summary>
/// Looks up a localized string similar to The user does not exist..
/// </summary>
internal static string UserNotExistException {
diff --git a/Timeline/Resources/Services/Exception.resx b/Timeline/Resources/Services/Exception.resx
index 3ae14d4e..2cb0f11a 100644
--- a/Timeline/Resources/Services/Exception.resx
+++ b/Timeline/Resources/Services/Exception.resx
@@ -135,6 +135,9 @@
<data name="BadPasswordException" xml:space="preserve">
<value>The password is wrong.</value>
</data>
+ <data name="ConfictException" xml:space="preserve">
+ <value>A present resource conflicts with the given resource.</value>
+ </data>
<data name="HashedPasswordBadFromatException" xml:space="preserve">
<value>The hashes password is of bad format. It might not be created by server.</value>
</data>
@@ -201,9 +204,6 @@
<data name="TimelineUserNotMemberException" xml:space="preserve">
<value>The use is not a member of the timeline.</value>
</data>
- <data name="UsernameConfictException" xml:space="preserve">
- <value>The username already exists.</value>
- </data>
<data name="UserNotExistException" xml:space="preserve">
<value>The user does not exist.</value>
</data>
diff --git a/Timeline/Resources/Services/UserManager.Designer.cs b/Timeline/Resources/Services/UserManager.Designer.cs
deleted file mode 100644
index 424499f8..00000000
--- a/Timeline/Resources/Services/UserManager.Designer.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UserManager {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UserManager() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.UserManager", typeof(UserManager).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user has been created..
- /// </summary>
- internal static string LogUserCreate {
- get {
- return ResourceManager.GetString("LogUserCreate", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/UserService.Designer.cs b/Timeline/Resources/Services/UserService.Designer.cs
index 1b85546d..cdf7f390 100644
--- a/Timeline/Resources/Services/UserService.Designer.cs
+++ b/Timeline/Resources/Services/UserService.Designer.cs
@@ -70,6 +70,15 @@ namespace Timeline.Resources.Services {
}
/// <summary>
+ /// Looks up a localized string similar to Nickname is of bad format, because {}..
+ /// </summary>
+ internal static string ExceptionNicknameBadFormat {
+ get {
+ return ResourceManager.GetString("ExceptionNicknameBadFormat", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Old username is of bad format..
/// </summary>
internal static string ExceptionOldUsernameBadFormat {
@@ -88,11 +97,11 @@ namespace Timeline.Resources.Services {
}
/// <summary>
- /// Looks up a localized string similar to Password can&apos;t be null or empty..
+ /// Looks up a localized string similar to Password can&apos;t be null..
/// </summary>
- internal static string ExceptionPasswordNullOrEmpty {
+ internal static string ExceptionPasswordNull {
get {
- return ResourceManager.GetString("ExceptionPasswordNullOrEmpty", resourceCulture);
+ return ResourceManager.GetString("ExceptionPasswordNull", resourceCulture);
}
}
@@ -106,29 +115,20 @@ namespace Timeline.Resources.Services {
}
/// <summary>
- /// Looks up a localized string similar to Username can&apos;t be null or empty..
- /// </summary>
- internal static string ExceptionUsernameNullOrEmpty {
- get {
- return ResourceManager.GetString("ExceptionUsernameNullOrEmpty", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A cache entry is created..
+ /// Looks up a localized string similar to A user with given username already exists..
/// </summary>
- internal static string LogCacheCreate {
+ internal static string ExceptionUsernameConflict {
get {
- return ResourceManager.GetString("LogCacheCreate", resourceCulture);
+ return ResourceManager.GetString("ExceptionUsernameConflict", resourceCulture);
}
}
/// <summary>
- /// Looks up a localized string similar to A cache entry is removed..
+ /// Looks up a localized string similar to Username can&apos;t be null..
/// </summary>
- internal static string LogCacheRemove {
+ internal static string ExceptionUsernameNull {
get {
- return ResourceManager.GetString("LogCacheRemove", resourceCulture);
+ return ResourceManager.GetString("ExceptionUsernameNull", resourceCulture);
}
}
diff --git a/Timeline/Resources/Services/UserService.resx b/Timeline/Resources/Services/UserService.resx
index 26221770..09bd4abb 100644
--- a/Timeline/Resources/Services/UserService.resx
+++ b/Timeline/Resources/Services/UserService.resx
@@ -120,26 +120,26 @@
<data name="ExceptionNewUsernameBadFormat" xml:space="preserve">
<value>New username is of bad format.</value>
</data>
+ <data name="ExceptionNicknameBadFormat" xml:space="preserve">
+ <value>Nickname is of bad format, because {}.</value>
+ </data>
<data name="ExceptionOldUsernameBadFormat" xml:space="preserve">
<value>Old username is of bad format.</value>
</data>
<data name="ExceptionPasswordEmpty" xml:space="preserve">
<value>Password can't be empty.</value>
</data>
- <data name="ExceptionPasswordNullOrEmpty" xml:space="preserve">
- <value>Password can't be null or empty.</value>
+ <data name="ExceptionPasswordNull" xml:space="preserve">
+ <value>Password can't be null.</value>
</data>
<data name="ExceptionUsernameBadFormat" xml:space="preserve">
<value>Username is of bad format, because {}.</value>
</data>
- <data name="ExceptionUsernameNullOrEmpty" xml:space="preserve">
- <value>Username can't be null or empty.</value>
- </data>
- <data name="LogCacheCreate" xml:space="preserve">
- <value>A cache entry is created.</value>
+ <data name="ExceptionUsernameConflict" xml:space="preserve">
+ <value>A user with given username already exists.</value>
</data>
- <data name="LogCacheRemove" xml:space="preserve">
- <value>A cache entry is removed.</value>
+ <data name="ExceptionUsernameNull" xml:space="preserve">
+ <value>Username can't be null.</value>
</data>
<data name="LogDatabaseCreate" xml:space="preserve">
<value>A new user entry is added to the database.</value>
diff --git a/Timeline/Services/ConfictException.cs b/Timeline/Services/ConfictException.cs
new file mode 100644
index 00000000..dcd77366
--- /dev/null
+++ b/Timeline/Services/ConfictException.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Timeline.Services
+{
+ /// <summary>
+ /// Thrown when a resource already exists and conflicts with the given resource.
+ /// </summary>
+ /// <remarks>
+ /// For example a username already exists and conflicts with the given username.
+ /// </remarks>
+ [Serializable]
+ public class ConfictException : Exception
+ {
+ public ConfictException() : base(Resources.Services.Exception.ConfictException) { }
+ public ConfictException(string message) : base(message) { }
+ public ConfictException(string message, Exception inner) : base(message, inner) { }
+ protected ConfictException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs
index c5595c99..616e70ba 100644
--- a/Timeline/Services/UserService.cs
+++ b/Timeline/Services/UserService.cs
@@ -21,7 +21,7 @@ namespace Timeline.Services
/// <param name="password">The password of the user to verify.</param>
/// <returns>The user info and auth info.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> or <paramref name="password"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when username is of bad format.</exception>
+ /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format or <paramref name="password"/> is empty.</exception>
/// <exception cref="UserNotExistException">Thrown when the user with given username does not exist.</exception>
/// <exception cref="BadPasswordException">Thrown when password is wrong.</exception>
Task<User> VerifyCredential(string username, string password);
@@ -48,7 +48,7 @@ namespace Timeline.Services
/// List all users.
/// </summary>
/// <returns>The user info of users.</returns>
- Task<User[]> ListUsers();
+ Task<User[]> GetUsers();
/// <summary>
/// Create a user with given info.
@@ -58,11 +58,12 @@ namespace Timeline.Services
/// <returns>The id of the new user.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="info"/>is null.</exception>
/// <exception cref="ArgumentException">Thrown when some fields in <paramref name="info"/> is bad.</exception>
- /// <exception cref="UsernameConfictException">Thrown when a user with given username already exists.</exception>
+ /// <exception cref="ConfictException">Thrown when a user with given username already exists.</exception>
/// <remarks>
/// <see cref="User.Username"/> must not be null and must be a valid username.
/// <see cref="User.Password"/> must not be null or empty.
/// <see cref="User.Administrator"/> is false by default (null).
+ /// <see cref="User.Nickname"/> must be a valid nickname if set. It is empty by default.
/// Other fields are ignored.
/// </remarks>
Task<long> CreateUser(User info);
@@ -75,61 +76,70 @@ namespace Timeline.Services
/// <exception cref="ArgumentException">Thrown when some fields in <paramref name="info"/> is bad.</exception>
/// <exception cref="UserNotExistException">Thrown when user with given id does not exist.</exception>
/// <remarks>
- /// Only <see cref="User.Administrator"/>, <see cref="User.Password"/> and <see cref="User.Nickname"/> will be used.
+ /// Only <see cref="User.Username"/>, <see cref="User.Administrator"/>, <see cref="User.Password"/> and <see cref="User.Nickname"/> will be used.
/// If null, then not change.
/// Other fields are ignored.
/// After modified, even if nothing is changed, version will increase.
///
- /// <see cref="User.Password"/> can't be empty.
+ /// <see cref="User.Username"/> must be a valid username if set.
+ /// <see cref="User.Password"/> can't be empty if set.
+ /// <see cref="User.Nickname"/> must be a valid nickname if set.
///
/// Note: Whether <see cref="User.Version"/> is set or not, version will increase and not set to the specified value if there is one.
/// </remarks>
+ /// <seealso cref="ModifyUser(string, User)"/>
Task ModifyUser(long id, User? info);
/// <summary>
- /// Partially modify a user of given username.
+ /// Modify a user's info.
+ /// </summary>
+ /// <param name="username">The username of the user.</param>
+ /// <param name="info">The new info. May be null.</param>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format or some fields in <paramref name="info"/> is bad.</exception>
+ /// <exception cref="UserNotExistException">Thrown when user with given id does not exist.</exception>
+ /// <remarks>
+ /// Only <see cref="User.Administrator"/>, <see cref="User.Password"/> and <see cref="User.Nickname"/> will be used.
+ /// If null, then not change.
+ /// Other fields are ignored.
+ /// After modified, even if nothing is changed, version will increase.
+ ///
+ /// <see cref="User.Username"/> must be a valid username if set.
+ /// <see cref="User.Password"/> can't be empty if set.
+ /// <see cref="User.Nickname"/> must be a valid nickname if set.
///
- /// Note that whether actually modified or not, Version of the user will always increase.
+ /// Note: Whether <see cref="User.Version"/> is set or not, version will increase and not set to the specified value if there is one.
+ /// </remarks>
+ /// <seealso cref="ModifyUser(long, User)"/>
+ Task ModifyUser(string username, User? info);
+
+ /// <summary>
+ /// Delete a user of given id.
/// </summary>
- /// <param name="username">Username of the user to modify. Can't be null.</param>
- /// <param name="password">New password. Null if not modify.</param>
- /// <param name="administrator">Whether the user is administrator. Null if not modify.</param>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="username"/> is null.</exception>
- /// <exception cref="UsernameBadFormatException">Thrown when <paramref name="username"/> is of bad format.</exception>
- /// <exception cref="UserNotExistException">Thrown if the user with given username does not exist.</exception>
- Task PatchUser(string username, string? password, bool? administrator);
+ /// <param name="id">Id of the user to delete.</param>
+ /// <returns>True if user is deleted, false if user not exist.</returns>
+ Task<bool> DeleteUser(long id);
/// <summary>
/// Delete a user of given username.
/// </summary>
- /// <param name="username">Username of thet user to delete. Can't be null.</param>
+ /// <param name="username">Username of the user to delete. Can't be null.</param>
+ /// <returns>True if user is deleted, false if user not exist.</returns>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="username"/> is null.</exception>
- /// <exception cref="UsernameBadFormatException">Thrown when <paramref name="username"/> is of bad format.</exception>
- /// <exception cref="UserNotExistException">Thrown if the user with given username does not exist.</exception>
- Task DeleteUser(string username);
+ /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
+ Task<bool> DeleteUser(string username);
/// <summary>
/// Try to change a user's password with old password.
/// </summary>
- /// <param name="username">The name of user to change password of.</param>
- /// <param name="oldPassword">The user's old password.</param>
- /// <param name="newPassword">The user's new password.</param>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="username"/> or <paramref name="oldPassword"/> or <paramref name="newPassword"/> is null.</exception>
- /// <exception cref="UsernameBadFormatException">Thrown when <paramref name="username"/> is of bad format.</exception>
+ /// <param name="id">The id of user to change password of.</param>
+ /// <param name="oldPassword">Old password.</param>
+ /// <param name="newPassword">New password.</param>
+ /// <exception cref="ArgumentNullException">Thrown if <paramref name="oldPassword"/> or <paramref name="newPassword"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown if <paramref name="oldPassword"/> or <paramref name="newPassword"/> is empty.</exception>
/// <exception cref="UserNotExistException">Thrown if the user with given username does not exist.</exception>
/// <exception cref="BadPasswordException">Thrown if the old password is wrong.</exception>
- Task ChangePassword(string username, string oldPassword, string newPassword);
-
- /// <summary>
- /// Change a user's username.
- /// </summary>
- /// <param name="oldUsername">The user's old username.</param>
- /// <param name="newUsername">The new username.</param>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="oldUsername"/> or <paramref name="newUsername"/> is null.</exception>
- /// <exception cref="UserNotExistException">Thrown if the user with old username does not exist.</exception>
- /// <exception cref="UsernameBadFormatException">Thrown if the <paramref name="oldUsername"/> or <paramref name="newUsername"/> is of bad format.</exception>
- /// <exception cref="UsernameConfictException">Thrown if user with the new username already exists.</exception>
- Task ChangeUsername(string oldUsername, string newUsername);
+ Task ChangePassword(long id, string oldPassword, string newPassword);
}
public class UserService : IUserService
@@ -138,11 +148,10 @@ namespace Timeline.Services
private readonly DatabaseContext _databaseContext;
-
private readonly IPasswordService _passwordService;
private readonly UsernameValidator _usernameValidator = new UsernameValidator();
-
+ private readonly NicknameValidator _nicknameValidator = new NicknameValidator();
public UserService(ILogger<UserService> logger, DatabaseContext databaseContext, IPasswordService passwordService)
{
_logger = logger;
@@ -150,17 +159,35 @@ namespace Timeline.Services
_passwordService = passwordService;
}
- private void CheckUsernameFormat(string username, string? paramName, Func<string, string>? messageBuilder = null)
+ private void CheckUsernameFormat(string username, string? paramName)
{
if (!_usernameValidator.Validate(username, out var message))
{
- if (messageBuilder == null)
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionUsernameBadFormat, message), paramName);
- else
- throw new ArgumentException(messageBuilder(message), paramName);
+ throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionUsernameBadFormat, message), paramName);
+ }
+ }
+
+ private static void CheckPasswordFormat(string password, string? paramName)
+ {
+ if (password.Length == 0)
+ {
+ throw new ArgumentException(ExceptionPasswordEmpty, paramName);
}
}
+ private void CheckNicknameFormat(string nickname, string? paramName)
+ {
+ if (!_nicknameValidator.Validate(nickname, out var message))
+ {
+ throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionNicknameBadFormat, message), paramName);
+ }
+ }
+
+ private static void ThrowUsernameConflict()
+ {
+ throw new ConfictException(ExceptionUsernameConflict);
+ }
+
private static User CreateUserFromEntity(UserEntity entity)
{
return new User
@@ -168,6 +195,7 @@ namespace Timeline.Services
Username = entity.Username,
Administrator = UserRoleConvert.ToBool(entity.Roles),
Nickname = string.IsNullOrEmpty(entity.Nickname) ? entity.Username : entity.Nickname,
+ Id = entity.Id,
Version = entity.Version
};
}
@@ -180,6 +208,7 @@ namespace Timeline.Services
throw new ArgumentNullException(nameof(password));
CheckUsernameFormat(username, nameof(username));
+ CheckPasswordFormat(password, nameof(password));
var entity = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync();
@@ -217,7 +246,7 @@ namespace Timeline.Services
return CreateUserFromEntity(entity);
}
- public async Task<User[]> ListUsers()
+ public async Task<User[]> GetUsers()
{
var entities = await _databaseContext.Users.ToArrayAsync();
return entities.Select(user => CreateUserFromEntity(user)).ToArray();
@@ -228,20 +257,22 @@ namespace Timeline.Services
if (info == null)
throw new ArgumentNullException(nameof(info));
- if (string.IsNullOrEmpty(info.Username))
- throw new ArgumentException(ExceptionUsernameNullOrEmpty, nameof(info));
-
+ if (info.Username == null)
+ throw new ArgumentException(ExceptionUsernameNull, nameof(info));
CheckUsernameFormat(info.Username, nameof(info));
- if (string.IsNullOrEmpty(info.Password))
- throw new ArgumentException(ExceptionPasswordNullOrEmpty);
+ if (info.Password == null)
+ throw new ArgumentException(ExceptionPasswordNull, nameof(info));
+ CheckPasswordFormat(info.Password, nameof(info));
+
+ if (info.Nickname != null)
+ CheckNicknameFormat(info.Nickname, nameof(info));
var username = info.Username;
var conflict = await _databaseContext.Users.AnyAsync(u => u.Username == username);
-
if (conflict)
- throw new UsernameConfictException(username);
+ ThrowUsernameConflict();
var administrator = info.Administrator ?? false;
var password = info.Password;
@@ -262,17 +293,35 @@ namespace Timeline.Services
return newEntity.Id;
}
- public async Task ModifyUser(long id, User? info)
+ private void ValidateModifyUserInfo(User? info)
{
- if (info != null && info.Password != null && info.Password.Length == 0)
- throw new ArgumentException(ExceptionPasswordEmpty, nameof(info));
+ if (info != null)
+ {
+ if (info.Username != null)
+ CheckUsernameFormat(info.Username, nameof(info));
- var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
- if (entity == null)
- throw new UserNotExistException(id);
+ if (info.Password != null)
+ CheckPasswordFormat(info.Password, nameof(info));
+
+ if (info.Nickname != null)
+ CheckNicknameFormat(info.Nickname, nameof(info));
+ }
+ }
+ private async Task UpdateUserEntity(UserEntity entity, User? info)
+ {
if (info != null)
{
+ var username = info.Username;
+ if (username != null)
+ {
+ var conflict = await _databaseContext.Users.AnyAsync(u => u.Username == username);
+ if (conflict)
+ ThrowUsernameConflict();
+
+ entity.Username = username;
+ }
+
var password = info.Password;
if (password != null)
{
@@ -293,82 +342,90 @@ namespace Timeline.Services
}
entity.Version += 1;
+ }
+
+
+ public async Task ModifyUser(long id, User? info)
+ {
+ ValidateModifyUserInfo(info);
+
+ var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
+ if (entity == null)
+ throw new UserNotExistException(id);
+
+ await UpdateUserEntity(entity, info);
await _databaseContext.SaveChangesAsync();
_logger.LogInformation(LogDatabaseUpdate, ("Id", id));
}
- public async Task DeleteUser(string username)
+ public async Task ModifyUser(string username, User? info)
{
if (username == null)
throw new ArgumentNullException(nameof(username));
- CheckUsernameFormat(username);
+ CheckUsernameFormat(username, nameof(username));
- var user = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync();
- if (user == null)
+ ValidateModifyUserInfo(info);
+
+ var entity = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync();
+ if (entity == null)
throw new UserNotExistException(username);
- _databaseContext.Users.Remove(user);
+ await UpdateUserEntity(entity, info);
+
await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(Log.Format(Resources.Services.UserService.LogDatabaseRemove,
- ("Id", user.Id)));
+ _logger.LogInformation(LogDatabaseUpdate, ("Username", username));
+ }
+
+ public async Task<bool> DeleteUser(long id)
+ {
+ var user = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
+ if (user == null)
+ return false;
- //clear cache
- await _cache.RemoveCache(user.Id);
+ _databaseContext.Users.Remove(user);
+ await _databaseContext.SaveChangesAsync();
+ _logger.LogInformation(Log.Format(LogDatabaseRemove, ("Id", id), ("Username", user.Username)));
+ return true;
}
- public async Task ChangePassword(string username, string oldPassword, string newPassword)
+ public async Task<bool> DeleteUser(string username)
{
if (username == null)
throw new ArgumentNullException(nameof(username));
- if (oldPassword == null)
- throw new ArgumentNullException(nameof(oldPassword));
- if (newPassword == null)
- throw new ArgumentNullException(nameof(newPassword));
- CheckUsernameFormat(username);
+ CheckUsernameFormat(username, nameof(username));
var user = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync();
if (user == null)
- throw new UserNotExistException(username);
-
- var verifyResult = _passwordService.VerifyPassword(user.Password, oldPassword);
- if (!verifyResult)
- throw new BadPasswordException(oldPassword);
+ return false;
- user.Password = _passwordService.HashPassword(newPassword);
- user.Version += 1;
+ _databaseContext.Users.Remove(user);
await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(Log.Format(Resources.Services.UserService.LogDatabaseUpdate,
- ("Id", user.Id), ("Operation", "Change password")));
- //clear cache
- await _cache.RemoveCache(user.Id);
+ _logger.LogInformation(Log.Format(LogDatabaseRemove, ("Id", user.Id), ("Username", username)));
+ return true;
}
- public async Task ChangeUsername(string oldUsername, string newUsername)
+ public async Task ChangePassword(long id, string oldPassword, string newPassword)
{
- if (oldUsername == null)
- throw new ArgumentNullException(nameof(oldUsername));
- if (newUsername == null)
- throw new ArgumentNullException(nameof(newUsername));
- CheckUsernameFormat(oldUsername, Resources.Services.UserService.ExceptionOldUsernameBadFormat);
- CheckUsernameFormat(newUsername, Resources.Services.UserService.ExceptionNewUsernameBadFormat);
-
- var user = await _databaseContext.Users.Where(u => u.Username == oldUsername).SingleOrDefaultAsync();
- if (user == null)
- throw new UserNotExistException(oldUsername);
+ if (oldPassword == null)
+ throw new ArgumentNullException(nameof(oldPassword));
+ if (newPassword == null)
+ throw new ArgumentNullException(nameof(newPassword));
+ CheckPasswordFormat(oldPassword, nameof(oldPassword));
+ CheckPasswordFormat(newPassword, nameof(newPassword));
- var conflictUser = await _databaseContext.Users.Where(u => u.Username == newUsername).SingleOrDefaultAsync();
- if (conflictUser != null)
- throw new UsernameConfictException(newUsername);
+ var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
- user.Username = newUsername;
- user.Version += 1;
- await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(Log.Format(Resources.Services.UserService.LogDatabaseUpdate,
- ("Id", user.Id), ("Old Username", oldUsername), ("New Username", newUsername)));
- await _cache.RemoveCache(user.Id);
- }
+ if (entity == null)
+ throw new UserNotExistException(id);
+ if (!_passwordService.VerifyPassword(entity.Password, oldPassword))
+ throw new BadPasswordException(oldPassword);
+ entity.Password = _passwordService.HashPassword(newPassword);
+ entity.Version += 1;
+ await _databaseContext.SaveChangesAsync();
+ _logger.LogInformation(Log.Format(LogDatabaseUpdate, ("Id", id), ("Operation", "Change password")));
+ }
}
}
diff --git a/Timeline/Services/UserTokenException.cs b/Timeline/Services/UserTokenException.cs
index e63305b1..ed0bae1a 100644
--- a/Timeline/Services/UserTokenException.cs
+++ b/Timeline/Services/UserTokenException.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
namespace Timeline.Services
{
diff --git a/Timeline/Services/UserTokenManager.cs b/Timeline/Services/UserTokenManager.cs
index a2c2980d..3e9ef3d4 100644
--- a/Timeline/Services/UserTokenManager.cs
+++ b/Timeline/Services/UserTokenManager.cs
@@ -62,7 +62,7 @@ namespace Timeline.Services
throw new ArgumentNullException(nameof(password));
var user = await _userService.VerifyCredential(username, password);
- var token = _userTokenService.GenerateToken(new UserTokenInfo { Id = user.Id, Version = user.Version, ExpireAt = expireAt });
+ var token = _userTokenService.GenerateToken(new UserTokenInfo { Id = user.Id!.Value, Version = user.Version!.Value, ExpireAt = expireAt });
return new UserTokenCreateResult { Token = token, User = user };
}
@@ -85,7 +85,7 @@ namespace Timeline.Services
var user = await _userService.GetUserById(tokenInfo.Id);
if (tokenInfo.Version < user.Version)
- throw new UserTokenBadVersionException(token, tokenInfo.Version, user.Version);
+ throw new UserTokenBadVersionException(token, tokenInfo.Version, user.Version.Value);
return user;
}
diff --git a/Timeline/Services/UsernameConfictException.cs b/Timeline/Services/UsernameConfictException.cs
deleted file mode 100644
index fde1eda6..00000000
--- a/Timeline/Services/UsernameConfictException.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using Timeline.Helpers;
-
-namespace Timeline.Services
-{
- /// <summary>
- /// Thrown when the user already exists.
- /// </summary>
- [Serializable]
- public class UsernameConfictException : Exception
- {
- public UsernameConfictException() : base(Resources.Services.Exception.UsernameConfictException) { }
- public UsernameConfictException(string username) : base(Log.Format(Resources.Services.Exception.UsernameConfictException, ("Username", username))) { Username = username; }
- public UsernameConfictException(string username, string message) : base(message) { Username = username; }
- public UsernameConfictException(string message, Exception inner) : base(message, inner) { }
- protected UsernameConfictException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- /// <summary>
- /// The username that already exists.
- /// </summary>
- public string? Username { get; set; }
- }
-}
diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs
index 379ce6ea..091a16e5 100644
--- a/Timeline/Startup.cs
+++ b/Timeline/Startup.cs
@@ -1,14 +1,11 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpOverrides;
-using Microsoft.AspNetCore.Localization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
-using System.Collections.Generic;
-using System.Globalization;
using System.Text.Json.Serialization;
using Timeline.Auth;
using Timeline.Configs;
@@ -89,7 +86,6 @@ namespace Timeline
services.AddTransient<IPasswordService, PasswordService>();
services.AddTransient<IClock, Clock>();
services.AddUserAvatarService();
- services.AddScoped<IUserDetailService, UserDetailService>();
services.AddScoped<IPersonalTimelineService, PersonalTimelineService>();
@@ -113,8 +109,6 @@ namespace Timeline
options.UseMySql(databaseConfig.ConnectionString);
});
}
-
- services.AddMemoryCache();
}
@@ -128,19 +122,6 @@ namespace Timeline
app.UseRouting();
- var supportedCultures = new List<CultureInfo>
- {
- new CultureInfo("en"),
- new CultureInfo("zh")
- };
-
- app.UseRequestLocalization(new RequestLocalizationOptions
- {
- DefaultRequestCulture = new RequestCulture("en"),
- SupportedCultures = supportedCultures,
- SupportedUICultures = supportedCultures
- });
-
app.UseCors();
app.UseAuthentication();
diff --git a/Timeline/Timeline.csproj b/Timeline/Timeline.csproj
index 195252d9..82b45094 100644
--- a/Timeline/Timeline.csproj
+++ b/Timeline/Timeline.csproj
@@ -82,10 +82,10 @@
<AutoGen>True</AutoGen>
<DependentUpon>Common.resx</DependentUpon>
</Compile>
- <Compile Update="Resources\Models\Validation\PasswordValidator.Designer.cs">
+ <Compile Update="Resources\Models\Validation\NicknameValidator.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
- <DependentUpon>PasswordValidator.resx</DependentUpon>
+ <DependentUpon>NicknameValidator.resx</DependentUpon>
</Compile>
<Compile Update="Resources\Models\Validation\UsernameValidator.Designer.cs">
<DesignTime>True</DesignTime>
@@ -117,11 +117,6 @@
<AutoGen>True</AutoGen>
<DependentUpon>UserDetailService.resx</DependentUpon>
</Compile>
- <Compile Update="Resources\Services\UserManager.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>UserManager.resx</DependentUpon>
- </Compile>
<Compile Update="Resources\Services\UserService.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
@@ -167,9 +162,9 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Common.Designer.cs</LastGenOutput>
</EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Validation\PasswordValidator.resx">
+ <EmbeddedResource Update="Resources\Models\Validation\NicknameValidator.resx">
<Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>PasswordValidator.Designer.cs</LastGenOutput>
+ <LastGenOutput>NicknameValidator.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Models\Validation\UsernameValidator.resx">
<Generator>ResXFileCodeGenerator</Generator>
@@ -195,10 +190,6 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>UserDetailService.Designer.cs</LastGenOutput>
</EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\UserManager.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UserManager.Designer.cs</LastGenOutput>
- </EmbeddedResource>
<EmbeddedResource Update="Resources\Services\UserService.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>UserService.Designer.cs</LastGenOutput>