diff --git a/TShockAPI/Rest.cs b/TShockAPI/Rest.cs
index 21b21924..7aa6b8a1 100644
--- a/TShockAPI/Rest.cs
+++ b/TShockAPI/Rest.cs
@@ -15,9 +15,9 @@ namespace TShockAPI
/// Rest command delegate
///
/// Parameters in the url
- /// Http request
+ /// {x} in urltemplate
/// Response object or null to not handle request
- public delegate object RestCommandD(RestVerbs verbs, IParameterCollection parameters, RequestEventArgs request);
+ public delegate object RestCommandD(RestVerbs verbs, IParameterCollection parameters);
public class Rest : IDisposable
{
readonly List commands = new List();
@@ -62,7 +62,7 @@ namespace TShockAPI
protected virtual void OnRequest(object sender, RequestEventArgs e)
{
- var obj = Process(sender, e);
+ var obj = ProcessRequest(sender, e);
if (obj == null)
throw new NullReferenceException("obj");
var str = JsonConvert.SerializeObject(obj, Formatting.Indented);
@@ -72,25 +72,40 @@ namespace TShockAPI
return;
}
- protected virtual object Process(object sender, RequestEventArgs e)
+ protected virtual object ProcessRequest(object sender, RequestEventArgs e)
{
foreach (var com in commands)
{
- var matches = Regex.Matches(e.Request.Uri.AbsolutePath, com.UriMatch);
- if (matches.Count == com.UriNames.Length)
+ var verbs = new RestVerbs();
+ if (com.HasVerbs)
{
- var verbs = new RestVerbs();
- for (int i = 0; i < matches.Count; i++)
- verbs.Add(com.UriNames[i], matches[i].Groups[1].Value);
+ var match = Regex.Match(e.Request.Uri.AbsolutePath, com.UriVerbMatch);
+ if (!match.Success)
+ continue;
+ if ((match.Groups.Count - 1) != com.UriVerbs.Length)
+ continue;
- var obj = com.Callback(verbs, e.Request.Parameters, e);
- if (obj != null)
- return obj;
+ for (int i = 0; i < com.UriVerbs.Length; i++)
+ verbs.Add(com.UriVerbs[i], match.Groups[i + 1].Value);
}
+ else if (com.UriTemplate.ToLower() != e.Request.Uri.AbsolutePath.ToLower())
+ {
+ continue;
+ }
+
+ var obj = ExecuteCommand(com, verbs, e.Request.Parameters);
+ if (obj != null)
+ return obj;
+
}
return new Dictionary { { "Error", "Invalid request" } };
}
+ protected virtual object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms)
+ {
+ return cmd.Callback(verbs, parms);
+ }
+
#region Dispose
public void Dispose()
{
@@ -123,18 +138,43 @@ namespace TShockAPI
public class RestCommand
{
+ public string Name { get; protected set; }
public string UriTemplate { get; protected set; }
- public string UriMatch { get; protected set; }
- public string[] UriNames { get; protected set; }
+ public string UriVerbMatch { get; protected set; }
+ public string[] UriVerbs { get; protected set; }
public RestCommandD Callback { get; protected set; }
+ public bool RequiesToken { get; set; }
- public RestCommand(string uritemplate, RestCommandD callback)
+ ///
+ ///
+ ///
+ /// Used for identification
+ /// Url template
+ /// Rest Command callback
+ public RestCommand(string name, string uritemplate, RestCommandD callback)
{
+ Name = name;
UriTemplate = uritemplate;
- UriMatch = string.Join("([^/]*)", Regex.Split(uritemplate, "\\{[^\\{\\}]*\\}"));
+ UriVerbMatch = string.Join("([^/]*)", Regex.Split(uritemplate, "\\{[^\\{\\}]*\\}"));
var matches = Regex.Matches(uritemplate, "\\{([^\\{\\}]*)\\}");
- UriNames = (from Match match in matches select match.Groups[1].Value).ToArray();
+ UriVerbs = (from Match match in matches select match.Groups[1].Value).ToArray();
Callback = callback;
+ RequiesToken = true;
+ }
+ ///
+ ///
+ ///
+ /// Url template
+ /// Rest Command callback
+ public RestCommand(string uritemplate, RestCommandD callback)
+ : this(string.Empty, uritemplate, callback)
+ {
+
+ }
+
+ public bool HasVerbs
+ {
+ get { return UriVerbs.Length > 0; }
}
}
}
diff --git a/TShockAPI/SecureRest.cs b/TShockAPI/SecureRest.cs
new file mode 100644
index 00000000..85c36ff2
--- /dev/null
+++ b/TShockAPI/SecureRest.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using HttpServer;
+
+namespace TShockAPI
+{
+ public delegate bool VerifyD(string username, string password);
+ public class SecureRest : Rest
+ {
+ public Dictionary Tokens { get; protected set; }
+ public event VerifyD Verify;
+ public SecureRest(IPAddress ip, int port)
+ : base(ip, port)
+ {
+ Tokens = new Dictionary();
+ Register(new RestCommand("/token/new/{username}/{password}", newtoken) { RequiesToken = false });
+ }
+ object newtoken(RestVerbs verbs, IParameterCollection parameters)
+ {
+ var user = verbs["username"];
+ var pass = verbs["password"];
+
+ if (Verify != null && !Verify(user, pass))
+ return new Dictionary { { "Error", "Failed to verify username/password" } };
+
+ string hash = string.Empty;
+ var rand = new Random();
+ var randbytes = new byte[20];
+ do
+ {
+ rand.NextBytes(randbytes);
+ hash = Tools.HashPassword(randbytes);
+ } while (Tokens.ContainsKey(hash));
+
+ Tokens.Add(hash, new Object());
+
+ return new Dictionary { { "Token", hash } }; ;
+ }
+ protected override object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms)
+ {
+ if (cmd.RequiesToken)
+ {
+ var strtoken = parms["token"];
+ if (strtoken == null)
+ return new Dictionary { { "Error", "Token Missing" } };
+
+ object token;
+ if (!Tokens.TryGetValue(strtoken, out token))
+ return new Dictionary { { "Error", "Token Invalid" } };
+ }
+ return base.ExecuteCommand(cmd, verbs, parms);
+ }
+ }
+}
diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs
index a4b0bb0b..555f17b6 100644
--- a/TShockAPI/TShock.cs
+++ b/TShockAPI/TShock.cs
@@ -176,7 +176,7 @@ namespace TShockAPI
Regions = new RegionManager(DB);
Itembans = new ItemManager(DB);
RememberedPos = new RemeberedPosManager(DB);
- RestApi = new Rest(IPAddress.Any, 8080);
+ RestApi = new SecureRest(IPAddress.Any, 8080);
RestManager = new RestManager(RestApi);
RestManager.RegisterRestfulCommands();
if (Config.EnableGeoIP)
diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj
index adadb917..2fac0775 100644
--- a/TShockAPI/TShockAPI.csproj
+++ b/TShockAPI/TShockAPI.csproj
@@ -126,6 +126,7 @@
+
@@ -182,7 +183,7 @@
-
+